动态生成LINQ查询

46
我们有一个对象。
public class SomeObject
{
   public Name {get;set;}
   public City {get;set;}
   public State {get;set}
   //various other parameters.  Let's say there's ~20
}

能否在不重新编译源代码的情况下动态创建新的LINQ查询?相反,查询参数来自存储和更新在数据库中的XML结构。

var result = from i in someObj
             where 
             //XML requests Name = 'Bob'...so append this where clause
             name = 'Bob'

这能做到吗?


2
有几种不同的方法可以做到这一点。它总是属性==值吗?小于、大于、不等于可能吗?XML是否可以指定多个值?例如Name = 'Bob' || Name = 'Fred'。条件只能用&&组合吗?例如Name = 'Bob' && State = 'OH'。还是条件可以用||组合?例如Name = 'Bob' || State = 'OH'。在提出解决方案之前,所有这些都需要考虑到。 - cadrell0
1
这个问题缺乏清晰度;它没有说明XML结构将包含条件表达式(而不仅仅是要匹配的期望值,如“Name ='Bob'”所暗示的那样)。很愚蠢和无益的是,有人认为适合四处下投票,打压所有没有推断出这个缺失信息的答案。 - Douglas
@Cadrell0 - 更复杂的查询将是一个非常好的补充。我试图从简单的where子句开始提出问题。我不知道这本身是否会是一个非常复杂的解决方案。根据答案,似乎有整个库可用来完成任务。ScottGU的动态LINQ看起来很有前途。 - P.Brian.Mackey
你应该尝试使用Gridify库:https://github.com/alirezanet/Gridify - AliReza Sabouri
7个回答

150

这里有一个利用表达式树的解决方案:

var param = Expression.Parameter(typeof(SomeObject), "p");
var exp = Expression.Lambda<Func<SomeObject, bool>>(
    Expression.Equal(
        Expression.Property(param, "Name"),
        Expression.Constant("Bob")
    ),
    param
);
var query = someObj.Where(exp);

我知道这个问题比较复杂,但这可能在某些时候会有用。


7
因为表达式树一开始可能有点难以理解,但它们是解决这个问题的好方法。一旦你掌握了最初的示例,就需要注意处理空值并确保表达式中使用的类型相匹配。根据查询源的“动态性”程度(例如,所有Expression.Constants()都作为字符串传入,即使它们与int进行比较),您可能需要进行一些额外的转换。这篇MSDN文章也是一个很好的参考 - http://msdn.microsoft.com/en-us/library/bb882637.aspx。 - Sam Storie
5
我爱你,伙计。你用两行代码简化了这个混乱的东西。让我指出someObj必须是IQueryable才能接受Where(exp) - Ivan Ferrer Villa
如果您想在等效的 Linq 查询中使用动态列名(在本例中为“NAME”),那么这个解决方案也非常好。 - Hamid
非常有用,但使用 LinqKit 库 https://github.com/scottksmith95/LINQKit 会更快。 - VinnyG
真希望我们能以这种方式构建查询语法(而不是Lambda语法)... - General Grievance

35

根据你的问题,很难确定,但在某些情况下,你不需要动态 Linq,只需这样做...

var result = from o in someObj 
             where (Name == null || o.Name == Name)
             && (City == null || o.City == City)
             && (State == null || o.State == State)
             select o;

这将基本上防止数据在相关参数为null时被过滤。由于C#中具有短路行为,所以性能依然很好。


4
你的想法给了我一个解决问题的线索,而不需要使用复杂的动态 Linq 库,我用传统的方法解决了它。谢谢。 - Ananda
这对我很有效。我忘记我们过去在SQL存储过程中使用它,所以很容易理解为什么它会在Linq中起作用。 - Caverman

30

你肯定想要看看动态 Linq,它允许你将查询条件定义为文本。

至于动态添加条件,你可以使用类似以下语法向查询中添加条件:

if(CategoryIsImportant)
    myQuery = myQuery.Where("CategoryId=2");

所有这些内容您都可以(相当容易地)编码为您选择的XML格式。


Where("SomeText") 是的,你完全理解了我想要实现的概念。 - P.Brian.Mackey
8
这是一篇较旧的帖子,可能与此特定问题无关,但动态 Linq 不是框架的内置功能。它是作为源代码发布的,您可以下载并编译到您的应用程序中(https://github.com/kahanu/System.Linq.Dynamic)。根据您的应用程序,这可能不合适,所以只想提高一些关于这个的意识。 - Sam Storie

9

1
可以,请你提供一个示例代码来展示如何使用动态linq吗? - jcolebrand
您可以在where子句中使用字符串,例如:query = query.Where("Id=123 And Age> 18"); - Antineutrino
7
伙计,难道我需要为你编辑吗?编辑你的帖子并包含那些信息。然后得到赞。然后进行游戏化。 - jcolebrand

9
我相信您需要深入了解表达式树。我还没有深入研究过这个问题,所以无法为您创建示例,但我知道您可以使用表达式树来动态构建查询,然后在代码中调用.Compile使其可运行。
实际上,这里有一个更好的链接使用表达式树构建动态查询。它应该能够给您想要的东西,并且对于它来说相当简洁。这应该是一个很好的例子 :)

Antineutrino的答案可能是最简单的,您可以传递字符串。但是,我认为Expression Tree中的.compile方法可以提供类型检查,因此在这个意义上它更安全一些。然而,根据我的了解,实现表达式树并不是微不足道的。 - Justin Pihony
4
这个为什么被投下了反对票?它肯定能够解决问题,所以我想知道为什么会被投反对票。 - Justin Pihony
1
表达式树 - 全家人都喜欢的乐趣 :) 获得点赞是因为其他解决方案甚至没有提到它们。 - Vladislav Zorov

5

是的,这实际上非常简单:

var name = GetBobNameFromXml();
var result = someObj.Where(i => i.Name == name);

您可以选择是否逐个应用标准。
var result = someObj;
var name = xmlCriteria.Name;
if(!string.IsNullOrEmpty(name))
{
    result = result.Where(i => i.Name == name);
}
// follow the same pattern for city, state, etc.

甚至可以使用一种模式,该模式使用命名键字的标准函数字典,以避免大量的if语句。
foreach(var criterionPair in xmlCriteria)
{
    var value = criterionPair.Value;
    result = result.Where(i => propGetters[criterionPair.PropertyName](i, value));
}

基本上,你可以做很多这样的事情。如果你想要一个更为特定于你情况的答案,你需要提供一个更为具体的问题。


不好意思,您错过了他的问题。您如何从文本字段创建LINQ查询? - jcolebrand
@jcolebrand:您能澄清一下“从文本字段创建linq查询”的意思吗?据我所知,考虑到问题的模糊性,我已经尽可能地回答了这个问题。 - StriplingWarrior

5

我猜你想引入可选过滤器,根据你的XML内容。接着使用StriplingWarrior的例子:

var name = GetNameFromXml();
var city = GetCityFromXml();
var state = GetStateFromXml();

var result = someObj;
if (name != null)
    result = result.Where(i => i.Name == name);
if (city != null)
    result = result.Where(i => i.City == city);
if (state != null)
    result = result.Where(i => i.State == state);

这样,您将根据XML中实际指定的内容应用任意数量的过滤器(从无到全部三个)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接