【问题标题】:Modularize (refactor) Linq queries模块化(重构)Linq 查询
【发布时间】:2011-08-24 14:14:04
【问题描述】:

我有一些 Linq 查询。从语义上讲,它们是

  1. a join b join c join d where filter1(a) && filter2(c) && filter3(d)

  2. a join b join c where filter1(a) && filter2(c)

  3. a 连接 b 连接 c 连接 e where filter1(a) && filter2(c) && filter4(e)

...

我希望能够排除共享部分:

a join b join c where filter1(a) && filter2(c)

并动态追加join d和filter3(d)

有没有办法做到这一点?我已经在使用 Predicate Builder 来动态构建条件(过滤器)。

编辑:我正在使用 Linq-to-SQL。

编辑:基本查询如下所示:

from a in As.AsExpandable()
join b in Bs on a.Id equals b.PId
join c in Cs on b.Id equals c.PId
where filter1(a) && filter2(b) && filter3(c)
select new A { ... }

过滤器是谓词生成器中的谓词。查询类型为IQueryable<A>。

接下来,我想和 d 一起参加这个活动

from a in BaseQuery()
join d in D on a.Id equals d.PId

目前join d .. 导致编译错误:

join 子句中的一个表达式的类型不正确。调用 Join 时类型推断失败

【问题讨论】:

  • 您使用的是哪个 Linq 提供程序?
  • @DoctaJonez:这真的重要吗?
  • @Steven 绝对是。如果提供者不是 Linq2Objects,很多事情都会中断。 Linq 提供程序模型目前充其量是随意的,特别是因为看到 Ayende 在 RavenDB 上花费了几个月的时间来实现它对开发人员来说是一场噩梦。
  • @Chris:也许我误解了这个问题,但请看看我的回答,告诉我这将如何影响不同的供应商。

标签: c# linq


【解决方案1】:

您的示例有点含糊,但如果您的意思是这样的话,创建一个返回 IQueryable<T> 的方法并重用该方法很容易。这是一个例子:

// Reusable method
public IQueryable<SomeObject> GetSomeObjectsByFilter(Context c)
{
     return
         from someObject in context.SomeObjects
         where c.B.A.Amount < 1000
         where c.Roles.Contains(r => r.Name == "Admin")
         select someObject;
}

你可以像这样在其他地方重用这个方法:

var q =
    from c in GetSomeObjectsByFilter(context)
    where !c.D.Contains(d => d.Items.Any(i => i.Value > 100))
    select c;

因为IQueryable 的工作方式,只有最终查询(您开始迭代的集合)会触发对数据库的调用,这允许您通过重用在内部有效执行的业务逻辑来构建高度可维护的系统数据库,排除任何性能损失。

我一直这样做,它大大提高了我的代码的可维护性。无论您运行哪种 O/RM 工具,它都可以工作,因为 Queryable&lt;T&gt; 的组合,无论是在一个和平中编写查询还是将其拆分为不同的方法,都没有区别。

请注意,有时您确实需要一些智能转换才能在单一方法中获取重复的部分。可能有帮助的事情是返回分组集合,并返回一组与您认为需要的不同类型的集合。这听起来有点模糊,但是当您在拆分方法时遇到问题时,只需在 SO 上发布一个问题。这里有足够多的人可以帮助您。

【讨论】:

  • 这个答案过于模糊,无法说明任何具体内容,但如果提供者是 Linq2Entities(又名 EntityFramework),这很可能不起作用,因为它会尝试在数据库中而不是在执行时执行您的方法.
  • 谢谢 Steven,我会测试你的解决方案。
  • @Steven:过滤器表达式可能会使 EF 出错。问题是filter(x) 不会编译,除非filter 是一个Func。查询提供程序需要 filter 成为 Expression 才能解析表达式树并转换为 SQL。您可以使用 AsExpandable 解决此问题,它是 LinqKit 的一部分。
  • @DoctaJonez:当 OP 的 filter1、filter2 和 filter3 是代表时,它确实不起作用。我没有这样解释他的问题。我只是假设filter1 只是他描述他需要 where 子句的方式。但现在我明白你和@Chris 的意思了,在这种情况下,你是绝对正确的:那绝对行不通。我将更改我的答案,使示例更清晰,不那么模棱两可。
  • @Candy:我不假设任何事情。仅显示将查询拆分为多种方法的一般方法。在您更新的情况下,让该方法返回一个IQueryable&lt;Tuple&lt;A, B&gt;&gt;。
【解决方案2】:

我可以轻松回答你一半的问题。 Linq 使将 .where 子句附加到现有查询变得简单。

例子:

var x = db.Table1.where(w => w.field1 == nTestValue);
x = x.where(w => w.field2 == nTestValue2);

我相信你也可以进行连接,但我必须在一些旧代码中找到一个示例。我会看看很快没有其他人加入。

【讨论】:

  • 我卡在了另一半。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多