【问题标题】:Can I clone an IQueryable in linq? For UNION purposes?我可以在 linq 中克隆 IQueryable 吗?出于联合目的?
【发布时间】:2010-06-14 20:45:34
【问题描述】:

我有一张WorkOrders 的表格。该表有一个PrimaryWorkerPrimaryPay 字段。它还有一个SecondaryWorker & SecondaryPay 字段(可以是null)。

我希望运行 2 个非常相似的查询并将它们合并,以便它返回一个 Worker 字段和 Pay 字段。因此,如果单个 WorkOrder 记录同时填充了 PrimaryWorkerSecondaryWorker 字段,我将返回 2 条记录。

这两个查询的“where 子句”部分非常相似,而且构造起来很长。这是一个虚拟示例

var q = ctx.WorkOrder.Where(w => w.WorkDate >= StartDt && w.WorkDate <= EndDt);

if(showApprovedOnly)
{
   q = q.Where(w => w.IsApproved);
}
//...more filters applied

现在我还有一个名为hideZeroPay 的搜索标志。如果这是真的,我不想在工人被支付 0 美元的情况下包括记录。但显然对于 1 个查询,我需要比较 PrimaryPay 字段,而在另一个查询中,我需要比较 SecondaryPay 字段。

所以我想知道如何做到这一点。

我可以克隆我的基本查询 q 并从中进行主要和次要工作人员查询,然后将这 2 个查询合并在一起吗?

【问题讨论】:

  • 如果能够为另一个 DbContext 实例克隆 IQueryable(没有人提供任何方法),那就太好了。就目前而言,如果您为一个 DbContext 实例构建一个复杂的查询,则无法将它重用于另一个 DbContext。您可以创建一个助手来构建它,但您仍然必须重新构建它。这似乎是很多不必要的开销。例如,如果您只想在同一个 IQueryable 上并行运行 CountAsync 和 Skip/Take/ToListAsync,则不能,因为要安全地并行运行它们,您需要两个单独的 DbContext 实例。

标签: c# linq entity-framework


【解决方案1】:

嗯,我不确定我是否理解你的意图。但我认为克隆是不必要的。为什么不从基本查询中拆分出两个新查询?


var baseQuery = ctx.WorkOrder.Where(w => w.WorkDate >= StartDt && w.WorkDate <= EndDt);

IQueryable<WorkOrder> query1;
if (showApprovedOnly)
{
  query1 = baseQuery.Where(w => w.IsApproved);
}
//more filters on query1
...

IQueryable<WorkOrder> query2;
if (/*something*/)
  query2 = baseQuery.Where(w => w.SomeThing);

定义查询后,您可以解释它们(按枚举)并检索不同的结果。


var res1 = query1.ToList();
var res2 = query2.ToList();

【讨论】:

  • 哦!出于某种原因,我在想我总是添加到同一个查询对象,但你说它实际上是每次调用像 where() 这样的 linq 函数时返回的一个新的不同查询对象。就像 String.Substring() 如何返回一个新的字符串和不一样的修改?如果是这样的话那就太好了。返回 query1 和 query2 的联合的最佳方式是什么?理想情况下,我希望它全部在数据库上运行。
  • 想了想,不得不说,你的想法也出现了。原因是 ObjectQuery 对象。好吧,当我写答案时,我只是在猜测,但现在我知道它有效。我不知道 .Union() 是否在数据库上执行。但是,在您尝试之后,我对您的结果很感兴趣。 ;o)
  • 是的,您似乎每次都会返回一个新的 ObjectQuery,因为 query1.equals(query2) 返回 false。我注意到在调用 Union() 之前我必须对每个查询执行我的投影( Select() )才能正常工作。然后,我在 ObjectQuery 上运行了一个 ToTraceString(),它确实可以在数据库上运行。
【解决方案2】:

当您执行第二次时,您实际上是在哪里克隆查询。

在这里你创建你的初始可查询对象。

var q = ctx.WorkOrder.Where(w => w.WorkDate >= StartDt && w.WorkDate <= EndDt);

在这里你创建一个新的可查询对象

if(showApprovedOnly)
{
   q = q.Where(w => w.IsApproved);
}
//...more filters applied

您需要做的就是创建一个新变量来存储修改后的查询。

var qw = q.Where(w=> w.IsApproved);

之所以可行,是因为可查询对象是作为对象创建的,并且查询本身仅在您枚举它时才会运行。

【讨论】:

    猜你喜欢
    • 2018-12-07
    • 2011-09-30
    • 2016-08-31
    • 1970-01-01
    • 2022-06-20
    • 2023-04-02
    • 2020-10-28
    • 1970-01-01
    • 2020-08-19
    相关资源
    最近更新 更多