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

本文关键字:属性 存储 表达式 多类型 OrderBy | 更新日期: 2023-09-27 18:11:06

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

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

稍后在泛型基类中使用的get:

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

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

this.OrderByString = c => c.CustomerID;

我不喜欢这样一个事实:我需要根据我想要排序的属性类型拥有单独的表达式。ToString不会在属性上工作,因为LINQ to Entities不支持它。我所追求的是一种存储表达式的方法,该表达式可以选择任何属性来排序,而不考虑类型。

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

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

无法强制转换类型"System"。Int32'类型'System.Object'。LINQto Entities只支持转换实体数据模型原语类型。

另外,如果我尝试一个小hack,这也不工作:

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

LINQ to Entities不能识别方法System。字符串ToString()'方法,并且该方法不能转换为store表达式。

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

听起来您想要一种在某个地方的列表中堆积一堆排序并应用它的方法。但是您不能这样做,因为每个表达式都有自己的类型,在调用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; } 

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

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

这样你就可以将where子句作为字符串保存或传递。没有大惊小怪,没有混乱。

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

Amy B的回答很好,我基于它提出了自己的解决方案。因此,我的观点更多的是对我所需要的东西的改进,我可能会及时改进。

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

使用type:

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

指定排序顺序:

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

应用排序:

ordered = OrderCoordinator.ApplyOrder(ordered); 

考虑使用方法代替属性。

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