如何使 Linq 扩展方法可用于实体?
How can I make Linq extension method available for the entities?
我写了一个扩展 System.Linq
class 的方法。我的 ContainsAnyWord
方法允许我针对一个字符串搜索字符串中的所有单词,而不是将一个字符串与另一个字符串进行比较。
下面是我写的扩展方法Linq
public static class LinqExtension
{
public static bool ContainsAnyWord(this string value, string searchFor)
{
if (!string.IsNullOrWhiteSpace(value) && !string.IsNullOrWhiteSpace(searchFor))
{
value = value.ToLower();
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return tags.Contains(value);
}
return false;
}
}
此扩展方法在 IEnumerable
对象上效果很好。但是当我想在 IQueryable
对象上使用它时,出现以下错误
LINQ to Entities does not recognize the method 'Boolean
ContainsAnyWord(System.String, System.String)' method, and this method
cannot be translated into a store expression.
以下示例效果很好,因为我使用的是列表。
using(var conn = new AppContext())
{
var allUsers = conn.Users.GetAll().ToList();
var foundUsers = allUsers.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
但是下面的示例不起作用,因为我正在使用 IQueryable
,这给了我上面列出的错误。
using(var conn = new AppContext())
{
var allUsers = conn.Users.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
如何解决此问题并使此方法可用于 IQueryable
对象?
已更新
根据我在下面得到的反馈,我尝试使用 IQueryable
来实现这个
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> query, string searchFor)
{
if (!string.IsNullOrWhiteSpace(searchFor))
{
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return query.Where(item => tags.Contains(item));
}
return query;
}
但这也给我一个错误
请记住,最后必须将其翻译为 SQL。
显然 ContainsAnyWord 不能翻译成 SQL...
所以,将您的名字保存在 List/Array 中并尝试
user => yourlist.Contains( user.FirstName)
EF 会将其转换为 WHERE ..IN
不需要方法,但如果出于某种原因你需要它
internal static class MyClass2
{
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> value, string searchFor) where T: User
{
var names = searchFor.Split(' ').ToList();
return value.Where(u => names.Contains(u.DisplayName));
}
}
你可以像这样使用它
var result=conn.Users.ContainsAnyWord("abc def ght");
Linq to Entities 将方法转换为数据库表达式。您不能实现 System.String 的扩展并期望它被翻译成 T-SQL 表达式,这是 Entity Framework 内部所做的。
我的建议是尝试使用预定义的 Linq 方法解决问题。尝试使用包含方法。
您不能在查询表达式中调用方法,Linq to Entites 提供的表达式解析器不知道如何处理。您唯一真正能做的就是编写一个扩展方法,该方法接受 IQueryable<T>
并在函数内部构建表达式,而不是调用 Where(
下面的代码完全未经测试,是我突然想到的。但是你需要做这样的事情
public static IQueryable<T> WhereContainsAnyWord<T>(this IQueryable<T> @this, Expression<Func<T, string>> selector, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
var selectedItems = @this.GroupBy(selector);
var filteredItems = selectedItems.Where(selectedItem => tags.Contains(selectedItem.Key.ToLower()));
var result = filteredItems.SelectMany(x => x);
return result;
}
像
一样使用
using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord(user => user.FirstName, "Some Full Name Goes Here").ToList();
}
这是一个更简单的非通用版本
public static IQueryable<User> WhereContainsAnyWord(this IQueryable<User> @this, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
return @this.Where(user => tags.Contains(user.DisplayName.ToLower()));
}
像
一样使用
using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord("Some Full Name Goes Here").ToList();
}
我写了一个扩展 System.Linq
class 的方法。我的 ContainsAnyWord
方法允许我针对一个字符串搜索字符串中的所有单词,而不是将一个字符串与另一个字符串进行比较。
下面是我写的扩展方法Linq
public static class LinqExtension
{
public static bool ContainsAnyWord(this string value, string searchFor)
{
if (!string.IsNullOrWhiteSpace(value) && !string.IsNullOrWhiteSpace(searchFor))
{
value = value.ToLower();
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return tags.Contains(value);
}
return false;
}
}
此扩展方法在 IEnumerable
对象上效果很好。但是当我想在 IQueryable
对象上使用它时,出现以下错误
LINQ to Entities does not recognize the method 'Boolean ContainsAnyWord(System.String, System.String)' method, and this method cannot be translated into a store expression.
以下示例效果很好,因为我使用的是列表。
using(var conn = new AppContext())
{
var allUsers = conn.Users.GetAll().ToList();
var foundUsers = allUsers.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
但是下面的示例不起作用,因为我正在使用 IQueryable
,这给了我上面列出的错误。
using(var conn = new AppContext())
{
var allUsers = conn.Users.Where
(
user =>
user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
|| user.LastName.ContainsAnyWord("Some Full Name Goes Here")
).ToList();
}
如何解决此问题并使此方法可用于 IQueryable
对象?
已更新
根据我在下面得到的反馈,我尝试使用 IQueryable
来实现这个
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> query, string searchFor)
{
if (!string.IsNullOrWhiteSpace(searchFor))
{
IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);
return query.Where(item => tags.Contains(item));
}
return query;
}
但这也给我一个错误
请记住,最后必须将其翻译为 SQL。
显然 ContainsAnyWord 不能翻译成 SQL...
所以,将您的名字保存在 List/Array 中并尝试
user => yourlist.Contains( user.FirstName)
EF 会将其转换为 WHERE ..IN
不需要方法,但如果出于某种原因你需要它
internal static class MyClass2
{
public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> value, string searchFor) where T: User
{
var names = searchFor.Split(' ').ToList();
return value.Where(u => names.Contains(u.DisplayName));
}
}
你可以像这样使用它
var result=conn.Users.ContainsAnyWord("abc def ght");
Linq to Entities 将方法转换为数据库表达式。您不能实现 System.String 的扩展并期望它被翻译成 T-SQL 表达式,这是 Entity Framework 内部所做的。
我的建议是尝试使用预定义的 Linq 方法解决问题。尝试使用包含方法。
您不能在查询表达式中调用方法,Linq to Entites 提供的表达式解析器不知道如何处理。您唯一真正能做的就是编写一个扩展方法,该方法接受 IQueryable<T>
并在函数内部构建表达式,而不是调用 Where(
下面的代码完全未经测试,是我突然想到的。但是你需要做这样的事情
public static IQueryable<T> WhereContainsAnyWord<T>(this IQueryable<T> @this, Expression<Func<T, string>> selector, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
var selectedItems = @this.GroupBy(selector);
var filteredItems = selectedItems.Where(selectedItem => tags.Contains(selectedItem.Key.ToLower()));
var result = filteredItems.SelectMany(x => x);
return result;
}
像
一样使用using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord(user => user.FirstName, "Some Full Name Goes Here").ToList();
}
这是一个更简单的非通用版本
public static IQueryable<User> WhereContainsAnyWord(this IQueryable<User> @this, string searchFor)
{
var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.
return @this.Where(user => tags.Contains(user.DisplayName.ToLower()));
}
像
一样使用using(var conn = new AppContext())
{
var allUsers = conn.Users.WhereContainsAnyWord("Some Full Name Goes Here").ToList();
}