通过实体框架中的或条件的动态查询
我正在创建一个搜索数据库的应用程序,并允许用户动态添加任何条件(约50个可能),就像以下问题一样:使用实体框架创建动态查询。我目前正在进行一个检查每个条件的搜索,如果不是空白,则将其添加到查询中。
c#
var query = Db.Names.AsQueryable();
if (!string.IsNullOrWhiteSpace(first))
query = query.Where(q => q.first.Contains(first));
if (!string.IsNullOrWhiteSpace(last))
query = query.Where(q => q.last.Contains(last));
//.. around 50 additional criteria
return query.ToList();
此代码在SQL Server中产生类似于以下的代码(我简化了以便于理解)
sql
SELECT
[Id],
[FirstName],
[LastName],
...etc
FROM [dbo].[Names]
WHERE [FirstName] LIKE '%first%'
AND [LastName] LIKE '%last%'
我现在正在尝试添加一种使用C#通过C#生成以下SQL的方法实体框架,但具有或,而不是和,同时仍保持动态添加标准的能力。
sql
SELECT
[Id],
[FirstName],
[LastName],
...etc
FROM [dbo].[Names]
WHERE [FirstName] LIKE '%first%'
OR [LastName] LIKE '%last%' <-- NOTICE THE "OR"
通常,标准不会大于两个或三个项目以进行查询,但是将它们组合成一个巨大的查询不是一个选择。我尝试了Concat,Union和Intersect,它们只是复制了查询,并与Union一起加入。
是否有一种简单干净的方法可以使用实体框架向动态生成的查询添加“或”条件?
使用我的解决方案编辑-9/29/2015
自发布此信息以来,我注意到这引起了一点关注,所以我决定发布我的解决方案
// Make sure to add required nuget
// PM> Install-Package LinqKit
var searchCriteria = new
{
FirstName = "sha",
LastName = "hill",
Address = string.Empty,
Dob = (DateTime?)new DateTime(1970, 1, 1),
MaritalStatus = "S",
HireDate = (DateTime?)null,
LoginId = string.Empty,
};
var predicate = PredicateBuilder.False<Person>();
if (!string.IsNullOrWhiteSpace(searchCriteria.FirstName))
{
predicate = predicate.Or(p => p.FirstName.Contains(searchCriteria.FirstName));
}
if (!string.IsNullOrWhiteSpace(searchCriteria.LastName))
{
predicate = predicate.Or(p => p.LastName.Contains(searchCriteria.LastName));
}
// Quite a few more conditions...
foreach(var person in this.Persons.Where(predicate.Compile()))
{
Console.WriteLine("First: {0} Last: {1}", person.FirstName, person.LastName);
}
I am creating an application that searches the database and allows the user to dynamically add any criteria (around 50 possible), much like the following SO question: Creating dynamic queries with entity framework. I currently have working a search that checks each criteria, and if it is not blank it adds it to the query.
C#
var query = Db.Names.AsQueryable();
if (!string.IsNullOrWhiteSpace(first))
query = query.Where(q => q.first.Contains(first));
if (!string.IsNullOrWhiteSpace(last))
query = query.Where(q => q.last.Contains(last));
//.. around 50 additional criteria
return query.ToList();
This code produces something similar to the following in sql server (I simplified for easier understanding)
SQL
SELECT
[Id],
[FirstName],
[LastName],
...etc
FROM [dbo].[Names]
WHERE [FirstName] LIKE '%first%'
AND [LastName] LIKE '%last%'
I am now trying to add a way to generate the following SQL with C# through entity framework but with an OR instead of an AND, while still keeping the ability to add criteria dynamically.
SQL
SELECT
[Id],
[FirstName],
[LastName],
...etc
FROM [dbo].[Names]
WHERE [FirstName] LIKE '%first%'
OR [LastName] LIKE '%last%' <-- NOTICE THE "OR"
Usually the criteria wont be larger than two or three items for a query but combining them into one gigantic query is not an option. I have tried concat, union, and intersect and they just all duplicate the query and join them with UNION.
Is there a simple and clean way to add "OR" conditions to a dynamically generated query using entity framework?
Edit with my solution - 9/29/2015
Since posting this, I have noticed this has received a little attention, so I decided to post my solution
// Make sure to add required nuget
// PM> Install-Package LinqKit
var searchCriteria = new
{
FirstName = "sha",
LastName = "hill",
Address = string.Empty,
Dob = (DateTime?)new DateTime(1970, 1, 1),
MaritalStatus = "S",
HireDate = (DateTime?)null,
LoginId = string.Empty,
};
var predicate = PredicateBuilder.False<Person>();
if (!string.IsNullOrWhiteSpace(searchCriteria.FirstName))
{
predicate = predicate.Or(p => p.FirstName.Contains(searchCriteria.FirstName));
}
if (!string.IsNullOrWhiteSpace(searchCriteria.LastName))
{
predicate = predicate.Or(p => p.LastName.Contains(searchCriteria.LastName));
}
// Quite a few more conditions...
foreach(var person in this.Persons.Where(predicate.Compile()))
{
Console.WriteLine("First: {0} Last: {1}", person.FirstName, person.LastName);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可能正在寻找谓词构建器,它允许您更轻松地控制Where语句。
还有 Dynamic Linq 使您可以像SQL字符串一样提交Where子句,并将其解析为正确的谓词。
You're probably looking for something like Predicate Builder which allows you to control the AND's and OR's of the where statement easier.
There's also Dynamic Linq which allows you to submit the WHERE clause like a SQL string and it will parse it into the correct predicate for a WHERE.
虽然Linqkit及其谓词builder的用途广泛,但可以更直接地使用一些简单的实用程序(每个实用程序)作为其他表达式操作操作的基础):
首先,通用表达式替换器:
Next,Next,a,简单的实用程序方法将一个参数的用法替换为给定表达式中的另一个参数:
这是必要的,因为两个不同表达式中的lambda参数实际上是不同的参数当他们有相同的名字时。例如,如果您想结束
q =&gt; q.first.contains(第一个)|| q.last.contains(last)
,然后q
inq.last.contains(last)
必须是完全相同的q
在lambda表达式开始时提供。接下来,我们需要一个通用
连接
方法,该方法能够连接func&lt; t,tretern&gt;
- 式lambda表达式以及给定的二进制表达式生成器。我们将与
expression.or
一起使用,但是您可以将相同的方法用于多种目的,例如将数字表达式与expression.ADD.ADD
相结合。最后,将所有内容放在一起,您可以拥有这样的东西:
While LINQKit and its PredicateBuilder are fairly versatile, it's possible to do this more directly with a few simple utilities (each of which can serve as the foundation for other Expression-manipulating operations):
First, a general-purpose Expression Replacer:
Next, a simple utility method to replace one parameter's usage with another parameter in a given expression:
This is necessary because the lambda parameters in two different expressions are actually different parameters, even when they have the same name. For example, if you want to end up with
q => q.first.Contains(first) || q.last.Contains(last)
, then theq
inq.last.Contains(last)
must be the exact sameq
that's provided at the beginning of the lambda expression.Next we need a general-purpose
Join
method that's capable of joiningFunc<T, TReturn>
-style Lambda Expressions together with a given Binary Expression generator.We'll use this with
Expression.Or
, but you could use the same method for a variety of purposes, like combining numeric expressions withExpression.Add
.Finally, putting it all together, you can have something like this:
是的,您可以通过简单地依靠一个子句,其中包含一个单个布尔表达式,其
或
parts在运行时动态“禁用”或“启用”,因此,避免必须安装linqkit或编写自定义谓词构建器。参考您的示例:
如上所述,我们正在动态切换
>
-filter表达式基于先前评估的前提的- 零件(例如)(例如
isfirstValid
)。例如,如果
iSfirstValid
不是true
,则name.first.contains(first)
is 短路,既不会执行也不会影响结果集。此外,EF Core的defaultquerysqlgenerator
将进一步优化并减少 内部的布尔表达式在执行之前(例如false&amp;&amp;&amp; x || true&amp;&amp; y y || &amp;&amp; z 可以通过简单的静态分析简单地 )。请注意:如果这些前提都不是
true
,那么结果集将是空的 - 我认为这是您的情况下的所需行为。但是,如果您出于某种原因而不是从iQueryable
源中选择所有元素,则可以将最终变量添加到true
(例如)中。 。
最后一句话:该技术的缺点是,它迫使您构建一个位于您查询所在的相同方法的“集中”布尔表达式(更准确地说是
,其中
查询的一部分)。如果您出于某种原因想将谓词的构建过程分散,并通过查询构建器将其注入参数或将其注入参数,那么您应该最好坚持使用其他答案中建议的谓词构建器。否则,请享受这种简单的技术:)Yes, you can achieve this by simply relying on a single
where
clause containing a single boolean expression whoseOR
parts are "disabled" or "enabled" dynamically at runtime, thus, avoiding having to install LINQKit or writing a custom predicate builder.In reference to your example:
As you can see in the example above, we are dynamically switching "on" or "off" the OR-parts of the
where
-filter expression based on previously evaluated premises (e.gisFirstValid
).For example if
isFirstValid
is nottrue
, thenname.first.Contains(first)
is short-circuited and will neither be executed nor affect the resultset. Moreover, EF Core'sDefaultQuerySqlGenerator
will further optimize and reduce the boolean expression insidewhere
before executing it (e.g.false && x || true && y || false && z
may be reduced to simplyy
through simple static analysis).Please note: If none of the premises are
true
, then the result-set will be empty – which I assume is the desired behavior in your case. However, if you for some reason rather prefer to select all elements from yourIQueryable
source, then you may add a final variable to the expression evaluating totrue
(e.g..Where( ... || shouldReturnAll)
withvar shouldReturnAll = !(isFirstValid || isLastValid)
or something similar).A final remark: The downside of this technique is that it forces you to build a "centralized" boolean expression that resides in the same method body in which your query lies (more precisely the
where
part of the query). If you, for some reason, want to decentralize the build process of your predicates and inject them as arguments or chain them via the query builder, then you should better stick with a predicate builder as suggested in the other answers. Otherwise, enjoy this simple technique :)striplingwarrior的答案,我写了我的linq扩展名来以linq的方式进行这项工作:
https://github.com/flithor/reusablecodes/blob/main/main/efcore/orpredicate.cs
代码(可能不是最新的):
如何使用:
享受!
Basedon StriplingWarrior's answer, I write my linq extension to do this work in linq way:
https://github.com/Flithor/ReusableCodes/blob/main/EFCore/OrPredicate.cs
The codes(may not newest):
How to use it:
Enjoy!