Store a layered OrderBy expression as a property

In a generic abstract base class, I store a couple of expressions used for ordering:

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

In the future, the base class is used:

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

Finally, one of them will be installed in the output of the constructor for a particular class:

 this.OrderByString = c => c.CustomerID; 

I do not like the fact that I need to have separate expressions based on the type of properties that I want for OrderBy. ToString will not work with this property because LINQ to Entities does not support it. What I need is a way to store an expression that selects any of the properties to order, regardless of type.

If I try something more general, for example:

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

Cannot enter type "System.Int32" to enter "System.Object". LINQ to Entities only supports listing of Entity Data Model primitive data types.

Also, if I try a little hack, this will not work either:

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

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

+6
source share
3 answers

It looks like you need a way to copy a bunch of order in a list somewhere and apply it. But you cannot, because each expression has its own type, which is checked by the compiler when calling OrderBy. You must have these two types when calling OrderBy, but you must have one type to be included in the same list.

Hide this second type behind the 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; } 
+7
source

This is easy to do if you are using the DynamicLinq library found on NuGet.org. This allows you to write queries such as:

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

This way you can save or pass in your sentences as strings. No fuss, no mousse.

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

+1
source

Consider using methods instead of properties.

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

Source: https://habr.com/ru/post/899111/


All Articles