欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

扩展 Entity Framework支持复杂的过滤条件(多个关键字模糊匹配)

程序员文章站 2024-03-06 08:18:01
之前遇到一个棘手的linq to ef查询的技术问题,现有产品表product,需要根据多个关键字模糊匹配产品名称, 现将解决方案分享出来。 问题描述 根据需求,我们需要编...
之前遇到一个棘手的linq to ef查询的技术问题,现有产品表product,需要根据多个关键字模糊匹配产品名称, 现将解决方案分享出来。

问题描述
根据需求,我们需要编写如下的sql语句来查询产品
复制代码 代码如下:

select * from dbo.product
where
(productname like 'product1%' or
productname like 'product2%')

如何将以上的sql语句转换成ef的写法呢?
方案一
可以使用union,将以上sql语句转换成以下的形式:
复制代码 代码如下:

select * from dbo.product
where
productname like 'product1%'
union
select * from docutapcms.dbo.product
where
productname like 'product2%'

然后将上路sql换成linq to ef就非常简单了,再此就不贴出来了。但每个条件都要写一个query,工作量大。如果条件太多,生成的sql语句也非常大,并且写起来很费力。

方案二
我们从linq to ef的contains功能得到启发,linq to ef 会将contains转换成in表达式。
那么我们可不可以直接写expression,将条件转换成上述sql语句呢?答案是肯定的。以下就是实现上述方案的具体linq to ef扩展。
复制代码 代码如下:

public static expression<func<telement, bool>> buildcontainsexpression<telement, tvalue>(expression<func<telement, tvalue>> valueselector,
  ienumerable<tvalue> values)
{
  var startswithmethod = typeof (string).getmethod("startswith", new[] { typeof(string) });
  var startwiths = values.select(value => (expression)expression.call(valueselector.body, startswithmethod, expression.constant(value, typeof(tvalue))));
  var body = startwiths.aggregate<expression>(((accumulate, equal) => expression.or(accumulate, equal)));
  var p = expression.parameter(typeof(telement));
  return expression.lambda<func<telement, bool>>(body, p);
}

用法:
复制代码 代码如下:

private static void queryproducts(iqueryable<product> query)
{
var productnames = new string[] {"p1", "p2"};
var query1 = from a in query.where(buildcontainsexpression<product, string>(d=>d.productname, productnames))
select a;
var items2 = query1.tolist();
}
private static void queryproducts(iqueryable<product> query)
{
var productnames = new string[] {"p1", "p2"};
var query1 = from a in query.where(buildcontainsexpression<product, string>(d=>d.productname, productnames))
select a;
var items2 = query1.tolist();
}

创建扩展方法,让调用变得简单
复制代码 代码如下:

public static iqueryable<telement> whereorlike<telement, tvalue>(this iqueryable<telement> query,
  expression<func<telement, tvalue>> valueselector, ienumerable<tvalue> values)
{
return query.where(buildcontainsexpression<telement, tvalue>(valueselector, values));
}
private static void queryproducts2(iqueryable<product> query)
{
var productnames = new string[] {"p1", "p2"};
query.whereorlike(d=>d.productname, productnames).tolist();
}

通过sql profile 监视生成的sql语句
复制代码 代码如下:

-- region parameters
declare @p0 nvarchar(3) = 'p1%'
declare @p1 nvarchar(3) = 'p2%'
-- endregion
select [t0].[id], [t0].[productname]
from [product] as [t0]
where ([t0].[productname] like @p0) or ([t0].[productname] like @p1)