如何使LINQ执行(SQL)LIKE范围搜索
我非常需要帮助,我一直试图这样做一段时间。
所以我有这个查询:
Select name from BlaBlaBla order by case when name like '9%' then 1 end, case when name like '8%' then 1 end, case when name like '7%' then 1 end, case when name like '6%' then 1 end, case when name like '5%' then 1 end, case when name like '4%' then 1 end, case when name like '3%' then 1 end, case when name like '2%' then 1 end, case when name like '1%' then 1 end, case when name like '0%' then 1 end, name
我想在一个新的C#,Asp.Net,类,在我的解决方案中,到域项目中实现它,所以它将是一个OrderTypefilter,对于某些function……
现在我有这个:
var param = Expression.Parameter(typeof(T), "item"); var paramName = Expression.Property(param, "Name"); var regexMatch = Expression.Constant("^[0-9]"); var startsWithDigit = Expression.Call(typeof(Regex), "IsMatch", null, paramName); var lambda = Expression.Lambda<Func>(startsWithDigit, param); return namesList.OrderBy(lambda) .ThenBy(BlaBla1()) .ThenByDescending(BlaBla2()) .ThenByDescending(BlaBla3()) .ThenBy(BlaBla4());
但它告诉我,Expression不包含“IsMatch”方法。
你能帮我么? 谢谢!!!
这里的问题是包含Regex
无法转换为SQL,因此即使您成功构建了正确的表达式,也无法在LINQ中将其用于SQL后端。 但是,SQL的LIKE
方法也支持范围通配符,如[0-9]
,所以诀窍是让你的LINQ转换为包含LIKE
语句的SQL。
LINQ-to-SQL提供了显式使用SQL LIKE
语句的可能性:
return namesList.OrderBy(r => SqlMethods.Like(r.Name, "[0-9]%")) ...
但是,此SqlMethods类只能在LINQ-to-SQL中使用。 在Entity Framework中,有一些字符串函数隐式转换为LIKE
,但它们都不启用范围通配符( [xy]
)。 在EF这样的声明……
return namesList.OrderBy(r => r.Name.StartsWith("[0-9]")) ...
……会转化为废话:
[Name] LIKE '~[0-9]%' ESCAPE '~'
也就是说它徒劳地寻找以文字字符串“[0-9]”开头的名字。 所以只要你继续使用LINQ-to-SQL SqlMethods.Like
就可以了。
在Entity Framework 6.1.3(及更低版本)中,我们必须使用稍微不同的方式来获得相同的结果……
return namesList.OrderBy(r => SqlFunctions.PatIndex("[0-9]%", c.Name) == 1) ...
…因为PatIndex
中的PatIndex也支持范围模式匹配。
但是在Entity Framwork 6.2中,由于新的DbFunctions.Like
function,我们又回到了LINQ-to-SQL的轨道上:
return namesList.OrderBy(r => DbFunctions.Like(r.Name, "[0-9]%")) ...
最后,Entity Framework核心还有一个Like
函数:
return namesList.OrderBy(r => EF.Functions.Like(r.Name, "[0-9]%")) ...
您可以在下面看到这种处理订单案例的方法示例。
static void Main(string[] args) { List list = new List (); for (int i = 0; i < 100; i++) { list.Add(new Obvious(i.ToString(), i)); } string name = list[30].name; switch (name) { case "9": list.OrderBy(o => o.perc) .ThenByDescending(o => o.name); break; default: list.OrderByDescending(o => o.name) .ThenBy(o => o.perc); break; } } public class Obvious { public string name { get; set; } public int perc { get; set; } public Obvious(string _name, int _perc) { this.name = _name; this.perc = _perc; } }
如果我是你,我不会尝试使用表达式解决这个问题,因为它带来了很多复杂性。
我看到你想要一个通用的方法,所以它可以使用不同的域实体,但你期望每个实体都有一个Name
属性。 您可以通过定义包含Name
属性的接口以更简单的方式解决此问题。 像这样:
public static void Main() { var test = new List() { new YourDomainEntity() { Name = "1test", OtherProperty = "1"}, new YourDomainEntity() { Name = "2test", OtherProperty = "2" }, new YourDomainEntity() { Name = "2test", OtherProperty = "1" } }; var k = Foo(test).ToList(); } public interface INameOrderable { string Name { get; set; } } public interface IOtherPropertyOrderable { string OtherProperty { get; set; } } public static IEnumerable Foo (IEnumerable list) where T : INameOrderable, IOtherPropertyOrderable { return list.OrderBy(a => a.Name, new NamesDescComparer()).ThenBy(b => b.OtherProperty); } public class NamesDescComparer : IComparer { public int Compare(string x, string y) => -String.CompareOrdinal(x, y); } class YourDomainEntity : INameOrderable, IOtherPropertyOrderable { public string OtherProperty { get; set; } public string Name { get; set; } }
我相信方法Foo
是你正在寻找的。
注意where T : INameOrderable
部分。 它将此方法的使用限制为实现INameOrderable
接口的实体