具有多个选择值的动态Linq查询 [英] Dynamic Linq Query with multiple select value's

查看:218
本文介绍了具有多个选择值的动态Linq查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在多个列上实现过滤器,但是我不想写 每列都有一个新查询.因此,我实现了一个如下所示的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆