将多类型OrderBy表达式存储为属性

时间:2011-10-12 08:41:34

标签: c# linq

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

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

稍后将在通用基类中使用:

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

最后,其中一个将在派生具体类的构造函数中设置:

this.OrderByString = c => c.CustomerID;

我不喜欢这样的事实,即我需要根据我想要OrderBy的属性类型使用单独的表达式。 ToString不适用于属性,因为LINQ to Entities不支持它。我所追求的是一种存储表达式的方法,该表达式选择任何要订购的属性而不管其类型。

如果我尝试一些更通用的东西,例如:

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

无法将类型'System.Int32'强制转换为'System.Object'。 LINQ   to Entities仅支持转换实体数据模型基元类型。

此外,如果我尝试轻微破解,这也不起作用:

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

LINQ to Entities无法识别方法'System.String   ToString()'方法,并且此方法无法转换为商店   表达

4 个答案:

答案 0 :(得分:12)

听起来你想要一种方法在一个列表中堆积一堆Ordering并应用它。但你不能因为每个Expression都有自己的类型,编译器在调用OrderBy时会检查它。调用OrderBy时必须有这两种类型,但是必须有一种类型放在同一个列表中。

隐藏界面后面的第二种类型。

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();
    }
}

在调试器中:

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

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

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

使用此属性

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

答案 1 :(得分:1)

如果您使用NuGet.org上的DynamicLinq库,这很容易做到。这允许您编写像;

这样的查询
db.People.Where("Id == 8");
db.People.OrderBy("Created ASC");

这样您就可以将where子句保存或传入字符串。没有大惊小怪,没有麻烦。

http://nuget.org/List/Packages/DynamicLINQ

答案 2 :(得分:1)

Amy B's答案很棒,我自己的解决方案就是基础。 所以我的观点更多的是我所需要的改进,我可能会及时改进。

public interface IOrderer<TItem>
{
    IOrderedQueryable<TItem> Apply(IQueryable<TItem> source);
}

public class OrderBy<TItem, TType> : IOrderer<TItem>
{
    private Expression<Func<TItem, TType>> _orderExpr;
    public OrderBy(Expression<Func<TItem, TType>> orderExpr)
    {
        _orderExpr = orderExpr;
    }

    public IOrderedQueryable<TItem> Apply(IQueryable<TItem> source)
    {
        return source.OrderBy(_orderExpr);
    }
}   

public class ThenBy<TItem, TType> : IOrderer<TItem>
{
    private Expression<Func<TItem, TType>> _orderExpr;
    public ThenBy(Expression<Func<TItem, TType>> orderExpr)
    {
        _orderExpr = orderExpr;
    }

    public IOrderedQueryable<TItem> Apply(IQueryable<TItem> source)
    {
        return ((IOrderedQueryable<TItem>)source).ThenBy(_orderExpr);
    }
}   

public class OrderCoordinator<TItem>
{
    public List<IOrderer<TItem>> Orders { get; private set; } = new List<IOrderer<TItem>>();

    public IQueryable<TItem> ApplyOrder(IQueryable<TItem> source)
    {
        foreach (IOrderer<TItem> orderer in Orders)
        {
            source = orderer.Apply(source);
        }
        return source;
    }

    public OrderCoordinator<TItem> OrderBy<TValueType>(Expression<Func<TItem, TValueType>> orderByExpression)
    {
        Orders.Add(new OrderBy<TItem, TValueType>(orderByExpression));
        return this;
    }

    // Can add more sort calls over time
    public OrderCoordinator<TItem> ThenBy<TValueType>(Expression<Func<TItem, TValueType>> orderByExpression)
    {
        Orders.Add(new ThenBy<TItem, TValueType>(orderByExpression));
        return this;
    }
}   

指定协调员,类型为:

public OrderCoordinator<MyObjectType> OrderCoordinator { get; private set; } = new OrderCoordinator<MyObjectType>();

指定排序顺序:

OrderCoordinator.OrderBy(e => e.MyStringProperty).ThenBy(e => e.MyIntProperty);

应用订购:

ordered = OrderCoordinator.ApplyOrder(ordered); 

答案 3 :(得分:0)

考虑使用方法而不是属性。

public abstract IEnumerable<T> ApplyOrdering( IEnumerable<T> q );

...

public override IEnumerable<T> ApplyOrdering( IEnumerable<T> q )
{
  return q.OrderBy( c => c.CustomerID );
}