我有一个在Entity Framework中动态创建查询的通用方法。 我将它用作数据表头的搜索功能。
如果Entity属性类型/ SQL数据类型是字符串,则该函数可以正常工作。这是因为.Contains()扩展。
当数据类型不是字符串时,问题就出现了。 这些数据类型没有.Contains()扩展名。
我希望能够在所有数据类型中使用此方法,并且发现我可以使用SqlFunctions.StringConvert。我也知道它没有整数选项,必须将基于整数的属性转换为double。
我不确定如何一般地实现SqlFunctions.StringConvert,请看下面的方法(你会看到我已经排除了没有.Contains()扩展名的数据类型):
public static IQueryable<T> Filter<T>(this IQueryable<T> query, List<SearchFilterDto> filters)
where T : BaseEntity
{
if (filters != null && filters.Count > 0 && !filters.Any(f => string.IsNullOrEmpty(f.Filter)))
{
Expression filterExpression = null;
ParameterExpression parameter = Expression.Parameter(query.ElementType, "item");
filterExpression = filters.Select(f =>
{
Expression selector = parameter;
Expression pred = Expression.Constant(f.Filter);
foreach (var member in f.Column.Split('.'))
{
PropertyInfo mi = selector.Type.GetProperty(member, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (mi != null)
{
selector = Expression.Property(selector, mi);
if (selector.Type == typeof(Guid) ||
selector.Type == typeof(Guid?) ||
selector.Type == typeof(DateTime) ||
selector.Type == typeof(DateTime?) ||
selector.Type == typeof(int) ||
selector.Type == typeof(int?)
)
{
return null;
}
}
else
{
return null;
}
}
Expression containsMethod = Expression.Call(selector, "Contains", null, pred);
return containsMethod;
}).Where(r => r != null).Aggregate(Expression.And);
LambdaExpression where = Expression.Lambda(filterExpression, parameter);
MethodInfo whereCall = (typeof(Queryable).GetMethods().First(mi => mi.Name == "Where" && mi.GetParameters().Length == 2).MakeGenericMethod(query.ElementType));
MethodCallExpression call = Expression.Call(whereCall, new Expression[] { query.Expression, where });
return query.Provider.CreateQuery<T>(call);
}
return query;
}
答案 0 :(得分:2)
如果Entity属性类型/ SQL数据类型是字符串,则该函数可以正常工作。这是因为.Contains()扩展。
我想提一下,Contains
在这种情况下不是扩展名,而是常规string.Contains
方法。
我希望能够在所有数据类型中使用此方法
这不是一个好主意,因为非字符串值可能有不同的字符串表示形式,因此您不太清楚您要搜索的内容。
但是假设你还是想要它。
并且发现我可以使用SqlFunctions.StringConvert
有两个缺点 - 首先,SqlFunctions
是特定于SqlServer的(例如,不像DbFunctions
),其次,StringConvert
仅适用于double
和{{1} }}。 IMO更好的选择是使用EF支持的decimal
方法(至少在最新的EF6中)。
我将根据object.ToString
为您提供解决方案。但在此之前,让我在处理表达式时给你一些提示。无论何时您想使用object.ToString()
构建表达式并且不知道如何,您可以构建类似的样本类型表达式并在调试器Locals / Watch窗口中进行检查。例如:
System.Linq.Expressions
你可以设置一个断点并开始扩展public class Foo
{
public int Bar { get; set; }
}
Expression<Func<Foo, bool>> e = item =>
SqlFunctions.StringConvert((decimal?)item.Bar).Contains("1");
成员,然后扩展他们的成员等等。你将看到编译器是如何构建表达式的,那么你所需要的只是找到相应的e
方法。
最后,这是解决方案本身。我还提供了一些小技巧,可以避免在可能的情况下直接使用反射和字符串方法名称:
Expression