存储多种类型的排序依据表达式作为一个属性 [英] Store multi-type OrderBy expression as a property

查看:160
本文介绍了存储多种类型的排序依据表达式作为一个属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个通用的抽象基类,我存储了几个用于排序的表达式:

 公共表达式来; Func键< T,串>> OrderByString {搞定;组; } 
公共表达式来; Func键< T,INT>> OrderByInt {搞定;组; }



获取后来用在通用基础类:

 如果(OrderByString!= NULL)
{
=结果results.OrderBy(OrderByString);
}
,否则如果(OrderByInt!= NULL)
{
=结果results.OrderBy(OrderByInt);
}



最后,他们中的一个将设置在派生具体类的建筑工:

  this.OrderByString = C => c.CustomerID; 



我不喜欢的事实,我需要根据属性类型我想有不同的表现形式为排序依据。的ToString不会对财产的,因为LINQ工作,实体不支持它。我后是存储的挑选任何属性的命令就不管类型的表达式的方法。



如果我尝试一些更通用的如

 公共表达式来; Func键< T,对象>>为了获得{;组; } 




无法投类型'System.Int32'输入' System.Object的。 LINQ
到实体仅支持铸造实体数据模型的基本类型。




此外,如果我尝试轻微的黑客,这也没有按' ŧ工作:

 公共表达式来; Func键< T,串>>为了获得{;组; } 
this.Order = C => c.OrderID.ToString();




LINQ到实体无​​法识别方法'System.String
的ToString()方法,而这种方法不能被翻译成店
的表达。



解决方案

听起来像是你想办法的地方堆放了一堆排序的列表,并使用它。但是你不能因为每个表达式都有它自己的类型,这是由编译器调用时排序依据检查。调用排序依据时,您必须拥有这两种类型,但你必须有一种把在同一列表。



隐藏接口背后的第二类。



 公共接口IOrderer< T> 
{
IOrderedQueryable< T> ApplyOrderBy(IQueryable的< T>源);
IOrderedQueryable< T> ApplyOrderByDescending(IQueryable的< T>源);
IOrderedQueryable< T> ApplyThenBy(IOrderedQueryable< T>源);
IOrderedQueryable< T> ApplyThenByDescending(IOrderedQueryable< T>源);
}

公共类订货人< T,U> :IOrderer< T>
{
私人表达式来; Func键< T,U>> _orderExpr;
公共订户(表达式来; Func键< T,U>> orderExpr){_orderExpr = orderExpr; }

公共IOrderedQueryable< T> ApplyOrderBy(IQueryable的< T>源)
{返回source.OrderBy(_orderExpr); }
公共IOrderedQueryable< T> ApplyOrderByDescending(IQueryable的< T>源)
{返回source.OrderByDescending(_orderExpr); }
公共IOrderedQueryable< T> ApplyThenBy(IOrderedQueryable< T>源)
{返回source.ThenBy(_orderExpr); }
公共IOrderedQueryable< T> ApplyThenByDescending(IOrderedQueryable< T>源)
{返回source.ThenByDescending(_orderExpr); }
}

公共类OrderCoordinator< T>
{
公开名单< IOrderer< T>>订单{搞定;组; }

公共OrderCoordinator(){订单=新的List< IOrderer< T>>(); }

//注意,没有返回IOrderedQueryable支持空订单
公众的IQueryable<返回的能力; T> ApplyOrders(IQueryable的< T>源)
{
的foreach(IOrderer< T>订货人的订单)
{
源= orderer.ApplyOrderBy(源);
}
收益来源;
}
}

公共类客户
{
公共字符串名称{;组; }
公众诠释FavNumber {搞定;组; }
}

公共类测试仪
{
公共无效测试()
{
OrderCoordinator<客户>坐标=新OrderCoordinator<客户>();
coord.Orders.Add(新订户<客户,串>(C => c.Name));
coord.Orders.Add(新订户<客户,INT>(C => c.FavNumber));

&IQueryable的LT;客户>查询= Enumerable.Empty<客户>()AsQueryable已()。

=查询coord.ApplyOrders(查询);

字符串结果= query.Expression.ToString();
}
}

在调试器:

 结果=OrderingDemo.Customer []排序依据(C => c.Name)。.OrderBy(C => c.FavNumber)






所以你的情况,而不是这个属性:

 公共表达式来; Func键< T,U>>为了获得{;组; } 

使用这个属性。



 公共IOrderer< T>为了获得{;组; } 


In a generic abstract base class I'm storing a couple of expressions used for ordering:

public Expression<Func<T, string>> OrderByString { get; set; }
public Expression<Func<T, int>> OrderByInt { get; set; }

The get used later on in the generic base class:

if (OrderByString != null)
{
    results = results.OrderBy(OrderByString);
}
else if (OrderByInt != null)
{
    results = results.OrderBy(OrderByInt);
}

Finally one of them will get set in the deriving concrete class's constructer:

this.OrderByString = c => c.CustomerID;

I don't like the fact that I need to have separate expressions based on the property type I want to OrderBy. ToString won't work on the property's because LINQ to Entities doesn't support it. What I'm after is a way of storing an expression that picks any of the properties to order on regardless of type.

If I try something a little more generic such as:

public Expression<Func<T, object>> Order { get; set; }

Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.

Additionally if I try a slight hack this also doesn't work:

public Expression<Func<T, string>> Order { get; set; }
this.Order = c => c.OrderID.ToString();

LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.

解决方案

Sounds like you want a way to pile up a bunch of Ordering in a list somewhere and apply it. But you can't because each Expression has its own type, which is checked by the compiler when calling OrderBy. You must have those two types when calling OrderBy, but you must have one type to put in the same list.

Hide that second type behind an interface.

public interface IOrderer<T>
{
    IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> source);
    IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> source);
    IOrderedQueryable<T> ApplyThenBy(IOrderedQueryable<T> source);
    IOrderedQueryable<T> ApplyThenByDescending(IOrderedQueryable<T> source);
}

public class Orderer<T, U> : IOrderer<T>
{
    private Expression<Func<T, U>> _orderExpr;
    public Orderer(Expression<Func<T, U>> orderExpr) { _orderExpr = orderExpr; }

    public IOrderedQueryable<T> ApplyOrderBy(IQueryable<T> source)
    { return source.OrderBy(_orderExpr); }
    public IOrderedQueryable<T> ApplyOrderByDescending(IQueryable<T> source)
    { return source.OrderByDescending(_orderExpr); }
    public IOrderedQueryable<T> ApplyThenBy(IOrderedQueryable<T> source)
    { return source.ThenBy(_orderExpr); }
    public IOrderedQueryable<T> ApplyThenByDescending(IOrderedQueryable<T> source)
    { return source.ThenByDescending(_orderExpr); }
}

public class OrderCoordinator<T>
{
    public List<IOrderer<T>> Orders { get; set; }

    public OrderCoordinator() { Orders = new List<IOrderer<T>>(); }

    //note, did not return IOrderedQueryable to support ability to return with empty Orders
    public IQueryable<T> ApplyOrders(IQueryable<T> source)
    {
        foreach (IOrderer<T> orderer in Orders)
        {
            source = orderer.ApplyOrderBy(source);
        }
        return source;
    }
}

public class Customer
{
    public string Name { get; set; }
    public int FavNumber { get; set; }
}

public class Tester
{
    public void Test()
    {
        OrderCoordinator<Customer> coord = new OrderCoordinator<Customer>();
        coord.Orders.Add(new Orderer<Customer, string>(c => c.Name));
        coord.Orders.Add(new Orderer<Customer, int>(c => c.FavNumber));

        IQueryable<Customer> query = Enumerable.Empty<Customer>().AsQueryable();

        query = coord.ApplyOrders(query);

        string result = query.Expression.ToString();
    }
}

In the debugger:

result = "OrderingDemo.Customer[].OrderBy(c => c.Name).OrderBy(c => c.FavNumber)"


So in your case, instead of this property:

 public Expression<Func<T, U>> Order { get; set; } 

use this property

 public IOrderer<T> Order { get; set; } 

这篇关于存储多种类型的排序依据表达式作为一个属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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