LINQ 到实体通用 == 解决方法

发布于 2024-12-12 03:17:50 字数 2032 浏览 0 评论 0原文

我有一个以下 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 的任何实体集合一起使用。

问题是由于内部查询中的 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 中与泛型进行相等比较?

I have a following LINQ-to-entities query

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 is a generic query. It can be obtained in the following way

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

In the end of all I want a generic query being able to work with any entity collection convertible to History<T>.

The problem is the code does not compile because of GenericId comparison in the inner query (Operator '==' cannot be applied to operands of type 'T' and 'T').

If I change == to h1.GenericId.Equals(h.GenericId) I get the following NotSupportedException:

Unable to cast the type 'System.Int64' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.

I've tried to do grouping instead of subquery and join the results.

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

It compiles because GenericId's are compared with equals keyword and it works but the query with real data is too slow (it has been running for 11 hours on dedicated postgresql server).

There is an option to build a whole expression for the outer where statement. But the code would be too long and unclear.

Are there any simple workarounds for equality comparison with generics in LINQ-to-entities?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

醉酒的小男人 2024-12-19 03:17:50

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

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

Try this, I think it should accomplish what you want without the extra query/join

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;
}
執念 2024-12-19 03:17:50
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;
}
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;
}
情域 2024-12-19 03:17:50

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

You could also set a generic constraint on T for an IItemHistory inteface that implements the GenericId and OperationId property.

对风讲故事 2024-12-19 03:17:50

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

My question already contains a solution. The second method with group + join works well if the table is properly indexed. It takes 3.28 seconds to retrieve 370k rows from the database table. In fact in non-generic variant the first query is slower on postgresql than the second one. 26.68 seconds vs 4.75.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文