实体框架4.1:无法从DbQuery强制转换为ObjectQuery

本文关键字:DbQuery 转换 ObjectQuery 框架 实体 | 更新日期: 2023-09-27 18:06:41

我有以下代码:

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());
}

基本上,我要做的是从上下文中删除大量数据(获取查询结果,获取SQL并执行它(。但是我在将result转换为ObjectQuery时遇到问题。给出的例外是

无法强制转换类型为的对象'System.Data.Entity.Infrastructure.DbQuery 1[System.Int32]' to type 'System.Data.Objects.ObjectQuery 1[System.Int32]'.

有人能给点提示来解决这个问题吗?谢谢

EDIT:Ladislav的第一个解决方案帮助我解决了这个问题,但生成的SQL查询的SQL参数出现了一个小问题,即query.ToString()生成的SQL询问是这样的:

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))

问题是变量@p__linq__0@p__linq__1没有声明,因此查询给出了错误"必须声明标量变量@p_linq_0"(我相信它会为变量@p__linq__1给出相同的错误(。为了"声明"它们,我需要将它们作为ExecuteSqlCommand()的参数传递。因此,最初答案的最终解决方案是下面的代码:

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)
    );
}

顺便说一句,我假设生成的变量总是@p__linq__格式,除非微软的实体框架团队在未来的EF更新中更改它。。。

实体框架4.1:无法从DbQuery强制转换为ObjectQuery

这是因为_database是从DbContext派生的,而AccountsOfMonthDbSet<>。在这种情况下,您不能直接使用ObjectQuery,因为DbSet<>产生的DbQuery<>不能转换为ObjectQuery<>

您必须直接使用DbQuery<>

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()
);

或者您必须首先将上下文转换为ObjectContext并创建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;

第一种方法的问题是DbQuery不提供Parameters集合——这只是DbContext API中简化的另一个例子,这只会使其更难使用。

在我的情况下,我真的需要参数,我找到了这个解决方法:

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...
}

我正在使用实体框架实现SqlCacheDependency。:-(