【问题标题】:Entity Framework 4.1: Unable to cast from DbQuery to ObjectQueryEntity Framework 4.1:无法从 DbQuery 转换为 ObjectQuery
【发布时间】:2011-08-26 17:55:33
【问题描述】:

我有以下代码:

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.DbQuery1[System.Int32]' to type 'System.Data.Objects.ObjectQuery1[System.Int32]'。

任何人都可以给出任何提示来解决这个问题吗?谢谢!

编辑: 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__ 格式,除非 Microsoft 的实体框架团队在未来的任何 EF 更新中更改它...

【问题讨论】:

    标签: c# entity-framework ef-code-first


    【解决方案1】:

    这是因为您的_database 派生自DbContext,而您的AccountsOfMonthDbSet&lt;&gt;。在这种情况下,您不能直接使用ObjectQuery,因为DbSet&lt;&gt; 生成的DbQuery&lt;&gt; 不能转换为ObjectQuery&lt;&gt;

    您必须直接使用DbQuery&lt;&gt;

    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&lt;&gt;

    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 中的另一个简化示例,它只会使其更难使用。

    【讨论】:

    • 感谢您的帮助!检查我的帖子的编辑(我添加了最终解决方案),因为 SQL 参数发生了一点问题。不管怎样,都解决了! :)
    【解决方案2】:

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

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

    【讨论】:

    • 在 EF6 中此代码仍然有效,但是您必须首先强制编译和缓存查询,然后再尝试检查参数,否则参数集合将为空!首先在原始查询上调用.ToString()
    猜你喜欢
    • 2011-06-13
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多