I have the following code:
public void DeleteAccountsForMonth(int year, int month)
{
var result = from acm in this._database.AccountsOnMonth
where ((acm.Year == year) && (acm.Month == month))
select acm.Id;
var query = (ObjectQuery<int>)result;
string sql = string.Format(
"DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
query.ToTraceString()
);
var parameters = new List<System.Data.SqlClient.SqlParameter>();
foreach (ObjectParameter parameter in query.Parameters)
{
parameters.Add(new System.Data.SqlClient.SqlParameter {
ParameterName = parameter.Name,
Value = parameter.Value
});
}
this._database.Database.ExecuteSqlCommand(sql, parameters.ToArray());
}
Basically, what I'm trying to do is to delete a bulk of data from a context (get a query result, get SQL and execute it). But I'm having a problem when casting result
to ObjectQuery
. The exception that gives is
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery
1[System.Int32]' to type 'System.Data.Objects.ObjectQuery
1[System.Int32]'.
Can anybody give any hint to solve this? Thanks!
EDIT: Ladislav first solution helped me solve the problem, but it happenned a little problem with the SQL parameters of the generated SQL query, i.e. the SQL query generated by query.ToString()
was this:
DELETE FROM [SncAccountOnMonths] WHERE [SncAccountOnMonths].[Id] IN (
SELECT [Extent1].[Id] AS [Id]
FROM [dbo].[SncAccountOnMonths] AS [Extent1]
WHERE ([Extent1].[Year] = @p__linq__0) AND ([Extent1].[Month] = @p__linq__1))
The problem was that the variables @p__linq__0
and @p__linq__1
where not declared and so the query gave the error "Must declare the scalar variable @p_linq_0" (I sure it would give the same error for variable @p__linq__1
). To "declare" them I need to pass them as arguments of the ExecuteSqlCommand()
. And so, the final solution for the initial answer is the code below:
public void DeleteAccountsForMonth(int year, int month)
{
var result = (this._database.AccountsOnMonth
.Where(acm => (acm.Year == year) && (acm.Month == month)))
.Select(acm => acm.Id);
var query = (DbQuery<int>)result;
string sql = string.Format(
"DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
query.ToString()
);
this._database.Database.ExecuteSqlCommand(sql,
new SqlParameter("p__linq__0", year),
new SqlParameter("p__linq__1", month)
);
}
By the way, I assume the variables generated always have the format @p__linq__
, unless Microsoft's Entity Framework Team changes it in any future EF update...
That is because your _database
is derived from DbContext
and your AccountsOfMonth
is DbSet<>
. In such case you cannot use ObjectQuery
directly because DbSet<>
produces DbQuery<>
which is not convertible to ObjectQuery<>
.
You must either use DbQuery<>
directly:
var result = from acm in this._database.AccountsOnMonth
where ((acm.Year == year) && (acm.Month == month))
select acm.Id;
var query = (DbQuery<int>)result;
string sql = string.Format(
"DELETE FROM [AccountsOnMonth] WHERE [AccountsOnMonth].[Id] IN ({0})",
query.ToString()
);
Or you must first convert your context to ObjectContext
and create ObjectSet<>
:
var objectContext = ((IObjectContextAdapter)_database).ObjectContext;
var set = objectContext.CreateObjectSet<AccountsOnMonth>();
var resut = from acm in set
where ((acm.Year == year) && (acm.Month == month))
select acm.Id;
The problem with first approach is that DbQuery
doesn't offer Parameters
collection - just another example of simplification in DbContext API which only makes it harder to use.
In my case I really needed the parameters and I found this workaround:
var query = (DbQuery<int>)result;
FieldInfo internalQueryField = query.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(f => f.Name.Equals("_internalQuery")).FirstOrDefault();
var internalQuery = internalQueryField.GetValue(query);
FieldInfo objectQueryField = internalQuery.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(f => f.Name.Equals("_objectQuery")).FirstOrDefault();
ObjectQuery<int> objectQuery = objectQueryField.GetValue(internalQuery) as ObjectQuery<int>;
foreach (ObjectParameter objectParam in objectQuery.Parameters)
{
SqlParameter sqlParam = new SqlParameter(objectParam.Name, objectParam.Value);
// Etc...
}
I'm implementing SqlCacheDependency with Entity Framework. :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With