具有多个选择值的动态Linq查询 [英] Dynamic Linq Query with multiple select value's
问题描述
我想在多个列上实现过滤器,但是我不想写 每列都有一个新查询.因此,我实现了一个如下所示的GetDistinctProperty函数:
I want to implement a filter on multiple columns, but I don't want to write for every column a new query. So I implemented a GetDistinctProperty function which looks like this :
public ActionResult GetDistinctProperty(string propertyName)
{
var selector = CreateExpression<TDomain>(propertyName);
var query = this.InventoryService.GetAll(Deal);
var results = query.Select(selector).Distinct().ToList();
return Json(results, JsonRequestBehavior.AllowGet);
}
private static Expression<Func<T, object>> CreateExpression<T>(string propertyName)
{
// Specify the type we are accessing the member from
var param = Expression.Parameter(typeof(T), "x");
Expression body = param;
// Loop through members in specified property name
foreach (var member in propertyName.Split('.'))
{
// Access each individual property
body = Expression.Property(body, member);
}
var conversion = Expression.Convert(body, typeof(object));
// Create a lambda of this MemberExpression
return Expression.Lambda<Func<T, object>>(conversion, param);
}
让我们以我作为propertyName SiteIdentifier作为示例.
Let's take as example that I have as propertyName SiteIdentifier.
选择器给我作为价值
{x => Convert(x.SiteIdentifier)}
,当我想查看结果时,出现以下错误:
and when I want to see the results it gives me the following error :
Unable to cast the type 'System.String' to type 'System.Object'.
LINQ to Entities only supports casting EDM primitive or enumeration types.
当我尝试如下选择时:
var results = query.Select(x=>x.SiteIdentifier).Distinct().ToList();
有效.
有任何想法吗?
推荐答案
问题是,尽管IQueryable<T>
接口是协变的,但值类型不支持协变,因此IQueryable<int>
不能视为IQueryable<object>
.另一方面,EF不喜欢将值类型强制转换为object
.
The problem is that although IQueryable<T>
interface is covariant, covariance is not supported for value types, so IQueryable<int>
cannot be treated as IQueryable<object>
. From the other side, EF does not like casting value type to object
.
因此,为了使其能够正常工作,您需要使用非通用的IQueryable
接口.不幸的是,几乎所有Queryable
扩展方法都是围绕IQueryable<T>
构建的,因此您必须手动编写相应的调用.
So in order to make it work, you need to resort to non generic IQueryable
interface. Unfortunately almost all Queryable
extension methods are build around IQueryable<T>
, so you have to manually compose a corresponding calls.
例如,要通过名称(路径)选择属性,您将需要以下内容:
For instance, in order to select property by name (path), you'll need something like this:
public static partial class QueryableExtensions()
{
public static IQueryable SelectProperty(this IQueryable source, string path)
{
var parameter = Expression.Parameter(source.ElementType, "x");
var property = path.Split(',')
.Aggregate((Expression)parameter, Expression.PropertyOrField);
var selector = Expression.Lambda(property, parameter);
var selectCall = Expression.Call(
typeof(Queryable), "Select", new[] { parameter.Type, property.Type },
source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery(selectCall);
}
}
但是您需要一个在IQueryable
上可用的Distinct
方法:
But then you'll need a Distinct
method that works on IQueryable
:
public static partial class QueryableExtensions()
{
public static IQueryable Distinct(this IQueryable source)
{
var distinctCall = Expression.Call(
typeof(Queryable), "Distinct", new[] { source.ElementType },
source.Expression);
return source.Provider.CreateQuery(distinctCall);
}
}
现在,您已经具有实现所讨论方法的所有必要步骤.但是,还有另一个重要的细节.为了能够创建List<object>
,您需要调用Cast<object>
.但是,如果使用IQueryable.Cast
扩展方法,则会从EF中获得相同的不受支持的异常.因此,您需要显式调用IEnumerable.Cast
代替:
Now you have all the necessary pieces to implement the method in question. But there is another important detail though. In order to be able to create List<object>
you need to call Cast<object>
. But if you use IQueryable.Cast
extension method you'll get the same not supported exception from EF. So you need to call explicitly the IEnumerable.Cast
instead:
public ActionResult GetDistinctProperty(string propertyName)
{
var query = this.InventoryService.GetAll(Deal);
var results = Enumerable.Cast<object>(
query.SelectProperty(propertyName).Distinct()).ToList();
return Json(results, JsonRequestBehavior.AllowGet);
}
这篇关于具有多个选择值的动态Linq查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!