【问题标题】:LINQ-to-entities generic == workaroundLINQ-to-entities 通用 == 解决方法
【发布时间】:2011-10-27 13:44:55
【问题描述】:

我有以下 LINQ-to-entities 查询

IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
{
    var q = (from h in ItemHistory
             where h.OperationId ==
                (from h1 in ItemHistory
                 where h1.GenericId == h.GenericId
                 select h1.OperationId).Min()
             select h);
    return q;
}

ItemHistory 是一个通用查询。可以通过以下方式获得

var history1 = MyEntitiySet1.Select(obj =>
    new History<long>{ obj.OperationId, GenericId = obj.LongId });
var history2 = AnotherEntitiySet.Select(obj =>
    new History<string>{ obj.OperationId, GenericId = obj.StringId });

最后,我希望通用查询能够处理可转换为History&lt;T&gt; 的任何实体集合。

问题是代码无法编译,因为内部查询中的 GenericId 比较(运算符“==”不能应用于“T”和“T”类型的操作数)。

如果我将 == 更改为 h1.GenericId.Equals(h.GenericId),我会得到以下 NotSupportedException

无法将类型“System.Int64”转换为类型“System.Object”。 LINQ to Entities 仅支持转换实体数据模型基元类型。

我尝试进行分组而不是子查询并加入结果。

IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
{
    var grouped = (from h1 in ItemHistory
                   group h1 by h1.GenericId into tt
                   select new
                   {
                        GenericId = tt.Key,
                        OperationId = tt.Min(ttt => ttt.OperationId)
                   });

    var q = (from h in ItemHistory
             join g in grouped
                on new { h.OperationId, h.GenericId }
                equals new { g.OperationId, g.GenericId }
             select h);
    return q;
}

它可以编译,因为 GenericId 与 equals 关键字进行了比较,它可以工作,但是使用真实数据的查询太慢(它已经在专用的 postgresql 服务器上运行了 11 个小时)。

有一个选项可以为外部 where 语句构建一个完整的表达式。但是代码会太长而且不清楚。

在 LINQ-to-entities 中与泛型进行相等比较是否有任何简单的解决方法?

【问题讨论】:

    标签: c# entity-framework entity-framework-4 linq-to-entities


    【解决方案1】:

    试试这个,我认为它应该可以完成你想要的,而不需要额外的查询/加入

    IQueryable<History<T>> GetFirstOperationsForEveryId<T>
        (IQueryable<History<T>> ItemHistory)
    {
      var q = from h in ItemHistory
              group h by h.GenericId into tt
              let first = (from t in tt
                            orderby t.GenericId
                            select t).FirstOrDefault()
              select first;
    
      return q;
    }
    

    【讨论】:

    • 相当优雅的解决方案!但不幸的是尚未在 Npgsql 中实现(截至 2.0.11.92)。 System.Data.EntityCommandCompilationException: An error occurred while preparing the command definition. ---&gt; System.NotImplementedException: The method or operation is not implemented. 我的实际问题是我忘记创建索引了。这就是为什么 group+join 查询工作了 11 个小时。有了索引,它的运行速度相当快。
    【解决方案2】:
    IQueryable<History<T>> GetFirstOperationsForEveryId<T>
    (IQueryable<History<T>> ItemHistory)
    {
    var grouped = (from h1 in ItemHistory
                   group t by h1.GenericId into tt
                   select new
                   {
                        GenericId = tt.Key,
                        OperationId = tt.Min(ttt => ttt.OperationId)
                   });
    
    var q = (from h in ItemHistory
             join g in grouped
                on new { h.OperationId, h.GenericId }
                equals new { g.OperationId, g.GenericId }
             select h);
    return q;
    }
    

    【讨论】:

    • 和我发的有什么区别?
    【解决方案3】:

    您还可以在 T 上为实现 GenericId 和 OperationId 属性的 IItemHistory 接口设置通用约束。

    【讨论】:

      【解决方案4】:

      我的问题已经包含解决方案。如果表被正确索引,使用 group + join 的第二种方法效果很好。从数据库表中检索 370k 行需要 3.28 秒。事实上,在非泛型变体中,postgresql 上的第一个查询比第二个查询慢。 26.68 秒 vs 4.75。

      【讨论】:

        猜你喜欢
        • 2010-09-27
        • 1970-01-01
        • 2011-12-07
        • 1970-01-01
        • 2013-10-23
        • 2023-04-06
        • 1970-01-01
        • 2015-08-25
        • 1970-01-01
        相关资源
        最近更新 更多