【问题标题】:Store multi-type OrderBy expression as a property将多类型 OrderBy 表达式存储为属性
【发布时间】:2018-05-01 17:40:33
【问题描述】:

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

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;

我不喜欢我需要根据我想要 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()' 方法,并且这个方法不能翻译成store 表达。

【问题讨论】:

  • 远射:你试过Expression&lt;Func&lt;T, object&gt;&gt;吗?
  • 感谢您的建议。我确实尝试过,但忘了提及。现在更新了问题。
  • 无论如何你都不想按字符串形式排序:22 在 3 之前

标签: c# linq


【解决方案1】:

听起来您想要一种方法来在某处的列表中堆积一堆 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; } 

【讨论】:

    【解决方案2】:

    如果您使用 NuGet.org 上的 DynamicLinq 库,这很容易做到。这使您可以编写如下查询;

    db.People.Where("Id == 8");
    db.People.OrderBy("Created ASC");
    

    这样,您可以将 where 子句作为字符串保存或传递。不要大惊小怪,不要大惊小怪。

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 基类本身进行排序,我从来没有持有我可以调用的它的实例。这是因为它是 WCF 操作。例如,在 Customer 类型的派生类中,WCF 调用将调用列表。 List 在此基类中实现,并将使用在 Customer 定义的构造函数中设置的 OrderBy。这个想法是,对于所有类型,产品、客户、订单,我不需要重新实现列表方法和所有相关的过滤。
        猜你喜欢
        • 2018-12-07
        • 1970-01-01
        • 1970-01-01
        • 2014-01-28
        • 1970-01-01
        • 2011-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多