List of Expression<Func<T, TProperty>> - c#

I'm searching a way to store a collection of Expression<Func<T, TProperty>> used to order elements, and then to execute the stored list against a IQueryable<T> object (the underlying provider is Entity Framework).
For example, I would like to do something like this (this is pseudo code):
public class Program
{
public static void Main(string[] args)
{
OrderClause<User> orderBys = new OrderClause<User>();
orderBys.AddOrderBy(u => u.Firstname);
orderBys.AddOrderBy(u => u.Lastname);
orderBys.AddOrderBy(u => u.Age);
Repository<User> userRepository = new Repository<User>();
IEnumerable<User> result = userRepository.Query(orderBys.OrderByClauses);
}
}
An order by clause (property on which to order):
public class OrderClause<T>
{
public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
{
_list.Add(orderBySelector);
}
public IEnumerable<Expression<Func<T, ???>>> OrderByClauses
{
get { return _list; }
}
}
A repository with my query method:
public class Repository<T>
{
public IEnumerable<T> Query(IEnumerable<OrderClause<T>> clauses)
{
foreach (OrderClause<T, ???> clause in clauses)
{
_query = _query.OrderBy(clause);
}
return _query.ToList();
}
}
My first idea was to convert the Expression<Func<T, TProperty>> into a string (the property name on which to sort). So basically, instead of storing a typed list (which is not possible because the TProperty is not constant), I store a list of string with the properties to sort on.
But this doesn't work because then I cannot reconstruct the Expression back (I need it because IQueryable.OrderBy takes a Expression<Func<T, TKey>> as parameter).
I also tried to dynamically create the Expression (with the help of Expression.Convert), to have a Expression<Func<T, object>> but then I got an exception from entity framework that said that it was not able to handle the Expression.Convert statement.
If possible, I do not want to use an external library like the Dynamic Linq Library.

This is one of the few cases where a dynamic / reflection solution may be appropriate.
I think you want something like this? (I've read between the lines and made some changes to your structure where I thought necessary).
public class OrderClauseList<T>
{
private readonly List<LambdaExpression> _list = new List<LambdaExpression>();
public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
{
_list.Add(orderBySelector);
}
public IEnumerable<LambdaExpression> OrderByClauses
{
get { return _list; }
}
}
public class Repository<T>
{
private IQueryable<T> _source = ... // Don't know how this works
public IEnumerable<T> Query(OrderClause<T> clauseList)
{
// Needs validation, e.g. null-reference or empty clause-list.
var clauses = clauseList.OrderByClauses;
IOrderedQueryable<T> result = Queryable.OrderBy(_source,
(dynamic)clauses.First());
foreach (var clause in clauses.Skip(1))
{
result = Queryable.ThenBy(result, (dynamic)clause);
}
return result.ToList();
}
}
The key trick is getting C# dynamic to do the horrible overload resolution and type-inference for us. What's more, I believe the above, despite the use of dynamic, is actually type-safe!

One way to do this would be to “store” all the sort clauses in something like Func<IQueryable<T>, IOrderedQueryable<T>> (that is, a function that calls the sorting methods):
public class OrderClause<T>
{
private Func<IQueryable<T>, IOrderedQueryable<T>> m_orderingFunction;
public void AddOrderBy<TProperty>(Expression<Func<T, TProperty>> orderBySelector)
{
if (m_orderingFunction == null)
{
m_orderingFunction = q => q.OrderBy(orderBySelector);
}
else
{
// required so that m_orderingFunction doesn't reference itself
var orderingFunction = m_orderingFunction;
m_orderingFunction = q => orderingFunction(q).ThenBy(orderBySelector);
}
}
public IQueryable<T> Order(IQueryable<T> source)
{
if (m_orderingFunction == null)
return source;
return m_orderingFunction(source);
}
}
This way, you don't have to deal with reflection or dynamic, all this code is type safe and relatively easy to understand.

You can store your lambda expressions in a collection as instances of the LambdaExpression type.
Or even better, store sort definitions, each of which, in addition to an expression, aslo stores a sorting direction.
Suppose you have the following extension method
public static IQueryable<T> OrderBy<T>(
this IQueryable<T> source,
SortDefinition sortDefinition) where T : class
{
MethodInfo method;
Type sortKeyType = sortDefinition.Expression.ReturnType;
if (sortDefinition.Direction == SortDirection.Ascending)
{
method = MethodHelper.OrderBy.MakeGenericMethod(
typeof(T),
sortKeyType);
}
else
{
method = MethodHelper.OrderByDescending.MakeGenericMethod(
typeof(T),
sortKeyType);
}
var result = (IQueryable<T>)method.Invoke(
null,
new object[] { source, sortDefinition.Expression });
return result;
}
and a similar method for ThenBy. Then you can do something like
myQueryable = myQueryable.OrderBy(sortDefinitions.First());
myQueryable = sortDefinitions.Skip(1).Aggregate(
myQueryable,
(current, sortDefinition) => current.ThenBy(sortDefinition));
Here are the definitions of SortDefinition and MethodHelper
public class SortDefinition
{
public SortDirection Direction
{
get;
set;
}
public LambdaExpression Expression
{
get;
set;
}
}
internal static class MethodHelper
{
static MethodHelper()
{
OrderBy = GetOrderByMethod();
ThenBy = GetThenByMethod();
OrderByDescending = GetOrderByDescendingMethod();
ThenByDescending = GetThenByDescendingMethod();
}
public static MethodInfo OrderBy
{
get;
private set;
}
public static MethodInfo ThenBy
{
get;
private set;
}
public static MethodInfo OrderByDescending
{
get;
private set;
}
public static MethodInfo ThenByDescending
{
get;
private set;
}
private static MethodInfo GetOrderByMethod()
{
Expression<Func<IQueryable<object>, IOrderedQueryable<object>>> expr =
q => q.OrderBy((Expression<Func<object, object>>)null);
return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
}
private static MethodInfo GetThenByMethod()
{
Expression<Func<IOrderedQueryable<object>, IOrderedQueryable<object>>> expr =
q => q.ThenBy((Expression<Func<object, object>>)null);
return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
}
private static MethodInfo GetOrderByDescendingMethod()
{
Expression<Func<IQueryable<object>, IOrderedQueryable<object>>> expr =
q => q.OrderByDescending((Expression<Func<object, object>>)null);
return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
}
private static MethodInfo GetThenByDescendingMethod()
{
Expression<Func<IOrderedQueryable<object>, IOrderedQueryable<object>>> expr =
q => q.ThenByDescending((Expression<Func<object, object>>)null);
return ((MethodCallExpression)expr.Body).Method.GetGenericMethodDefinition();
}
}

in E.F. Core you could use the following helper class
public static class OrderBuilder
{
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> queryable, params Tuple<Expression<Func<TSource, object>>, bool>[] keySelectors)
{
if (keySelectors == null || keySelectors.Length == 0) return queryable;
return keySelectors.Aggregate(queryable, (current, keySelector) => keySelector.Item2 ? current.OrderDescending(keySelector.Item1) : current.Order(keySelector.Item1));
}
private static bool IsOrdered<TSource>(this IQueryable<TSource> queryable)
{
if (queryable == null) throw new ArgumentNullException(nameof(queryable));
return queryable.Expression.Type == typeof(IOrderedQueryable<TSource>);
}
private static IQueryable<TSource> Order<TSource, TKey>(this IQueryable<TSource> queryable, Expression<Func<TSource, TKey>> keySelector)
{
if (!queryable.IsOrdered()) return queryable.OrderBy(keySelector);
var orderedQuery = queryable as IOrderedQueryable<TSource>;
return (orderedQuery ?? throw new InvalidOperationException()).ThenBy(keySelector);
}
private static IQueryable<TSource> OrderDescending<TSource, TKey>(this IQueryable<TSource> queryable, Expression<Func<TSource, TKey>> keySelector)
{
if (!queryable.IsOrdered()) return queryable.OrderByDescending(keySelector);
var orderedQuery = queryable as IOrderedQueryable<TSource>;
return (orderedQuery ?? throw new InvalidOperationException()).ThenByDescending(keySelector);
}
}
and then use it as.. this example below with a class named Player with the following members..(code reduced for brevity)
public class Player
{
...
public string FirstName { get; set;
public int GenderTypeId { get; set; }
public int PlayingExperience { get; set; }
You could combine the orderings as you like, sorting by gender, playing experience descending (notice the true value of the tuple), and first name..
var combinedOrder = new[]
{
new Tuple<Expression<Func<Player, object>>, bool>(p => p.GenderTypeId, false),
new Tuple<Expression<Func<Player, object>>, bool>(p => p.PlayingExperience, true),
new Tuple<Expression<Func<Player, object>>, bool>(p => p.FirstName, false),
};
and just do the order as follows
var data = context.Set<Player>()
.OrderBy(combinedOrder)
.ToArray();

Related

How to make all fields search in ASP.Net Api

I have this Get method:
[HttpGet]
public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers(string s, int page, int page_size)
{
IQueryable<Customer> query = _context.Customers;
if (!string.IsNullOrEmpty(s))
{
var stringProperties = typeof(Customer).GetProperties();
query = query.Where(c => stringProperties.Any(prop => prop.GetValue(c, null).ToString().Contains(s)));
}
return await query.Skip((page - 1) * page_size).Take(page_size).ToListAsync();
}
But my realization of search doesn't work, I get this error
.Where(m => __stringProperties_0 .Any(prop => prop.GetValue(obj: m, index: null).ToString().Contains(__8__locals1_s_1))) could not be
translated
How can I fix this?
Well, basic mistake that you are dealing with PropertyInfo.GetValue. On the SQL server side you do not have objects and cannot get values in that way. What you ca do it is instruct EF how compare properties.
I have created universal function, which may help to create such queries. Instead of just dealing with strings it can be adopted to any type.
Also consider to use extensions, if it is possible.
public static IQueryable<Customer> FilterCustomers(this IQueryable<Customer> query, string s)
{
if (!string.IsNullOrEmpty(s))
{
query = query.FilterByProperties(s, (prop, value) => prop.Contains(value), true)
}
return query;
}
public static Task<List<T>> PaginateAsync<T>(this IQueryable<T> query, int page, int page_size)
{
return query.Skip((page - 1) * page_size).Take(page_size).ToListAsync();
}
[HttpGet]
public Task<List<Customer>> GetCustomers(string s, int page, int page_size)
{
var query = _context.Customers.FilterCustomers(s);
return query.PaginateAsync(page, page_size);
}
And implementation:
public static class QueryableExtensions
{
public static Expression<Func<T, bool>> MakePropertiesPredicate<T, TValue>(Expression<Func<TValue, TValue, bool>> pattern, TValue searchValue, bool isOr)
{
var parameter = Expression.Parameter(typeof(T), "e");
var searchExpr = Expression.Constant(searchValue);
var predicateBody = typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(TValue))
.Select(p =>
ExpressionReplacer.GetBody(pattern, Expression.MakeMemberAccess(
parameter, p), searchExpr))
.Aggregate(isOr ? Expression.OrElse : Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(predicateBody, parameter);
}
public static IQueryable<T> FilterByProperties<T, TValue>(this IQueryable<T> query, TValue searchValue,
Expression<Func<TValue, TValue, bool>> pattern, bool isOr)
{
return query.Where(MakePropertiesPredicate<T, TValue>(pattern, searchValue, isOr));
}
class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression node)
{
if (node != null && _replaceMap.TryGetValue(node, out var replacement))
return replacement;
return base.Visit(node);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression)lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
}

Conditionally call Linq extension method

I have an abstract class that implement all of my queries. My Get function is:
public virtual List<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
return _dbSet.Where(criteria).OrderByDescending(OrderBy).ThenBy(ThenOrderBy).ToList();
}
And the ThenOrderBy property is:
public virtual Func<TEntity, object> ThenOrderBy { get; set; }
The OrderBy property is required, but my new property, ThenOrderBy is not required and not implemented in all classes that inherits this abstract class.
I am getting this error:
Value can not be null
Is there any way to mantain this clean code without putting blocks of "if's" statments?
Solution that I Used:
public virtual List<TEntity> Consultar(Expression<Func<TEntity, bool>> criteria)
{
var query = _dbSet.Where(criteria);
query = OrderDescending ?
query.OrderByDescending(OrderBy).AndOptionallyBy(ThenOrderBy)
:
query.OrderBy(OrderBy).AndOptionallyBy(ThenOrderBy);
query = (paggedSearch && Skip > 0) ? query.Skip(Skip) : query;
query = (paggedSearch && Take > 0) ? query.Take(Take) : query;
return query.ToList();
}
And Created a new Extension Class
public static class ExtensionMethods
{
public static IOrderedQueryable<TSource> AndOptionallyBy<TSource, TKey>(this IOrderedQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return (keySelector == null) ? source : source.ThenBy(keySelector);
}
}
No, you need to block it with an if. However, you can build up your EF queries without hitting the database:
public virtual List<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
var query = _dbSet.Where(criteria);
if(OrderBy != null)
{
query = query.OrderByDescending(OrderBy);
if(ThenOrderBy != null)
{
query = query.ThenBy(ThenOrderBy);
}
}
return query.ToList();
}
If short then:
public virtual List<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
var result = _dbSet.Where(criteria).OrderByDescending(OrderBy);
if(ThenOrderBy != null)
{
result = result.ThenBy(ThenOrderBy);
}
return result.ToList();
}
You could create your own AndOptionallyBy method that applies the second condition if supplied, or leaves the IOrderedQueryable as is otherwise, eg:
public static IOrderedQueryable<TSource> AndOptionallyBy<TSource, TKey>(
this IOrderedQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector)
{
if (keySelector==null)
{
return source;
}
else
{
return source.ThenBy(keySelector);
}
}
I wouldn't use this though. While this allows you to use function chaining, it will surprise maintainers of your code.
On the one hand you have :
var result = _dbSet.Where(criteria)
.OrderByDescending(OrderBy)
.AndOptionallyBy(ThenOrderBy)
.ToList();
On the other
var query = _dbSet.Where(criteria)
.OrderByDescending(OrderBy);
if (ThenOrderBy!=null)
{
query = query.ThenBy(ThenOrderBy);
}
var result = query.ToList();
Which do you think is clearer to someone else?
UPDATE
It's easy to create a paging query using conditionals:
var query = _dbSet.Where(criteria);
var orderedQuery=OrderDescending
?query.OrderByDescending(OrderBy)
:query.OrderBy(OrderBy);
if (buscaPaginada)
{
if (Skip > 0)
{
query = query.Skip(Skip);
}
if (Take >0)
{
query = query.Skip(Skip);
}
}
return query.ToList();
Sometimes the cleanest solution is using a bunch of if() statements. In this case you can check whether the ThenOrderBy is null:
public virtual List<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
IQueryable<TEntity> recordsToReturn = _dbSet.Where(criteria);
if (OrderBy != null)
{
var orderedRecordsToReturn = recordsToReturn.OrderByDescending(OrderBy);
recordsToReturn = orderedRecordsToReturn;
// You can only call ThenBy() on an IOrderedQueryable
if (ThenOrderBy != null)
{
recordsToReturn = orderedRecordsToReturn.ThenBy(ThenOrderBy);
}
}
return recordsToReturn.ToList();
}
Sure, there are ways to abuse syntax to condense this code, but being explicit about what is happening makes code easier to read, hence cleaner.
If you want the ThenBy to have a default behavior then just default it to something which will have no effecton the ordering instead of null. For example entity => 1
I tested it like the below, which allows me to not set OrderBy or ThenOrderBy and it still all works as expected:
public class TestClass<TEntity>
{
private IEnumerable<TEntity> data;
public TestClass(IEnumerable<TEntity> data){
OrderBy = (t) => 1;
ThenOrderBy = (t) => 1;
this.data = data;
}
public IEnumerable<TEntity> Get(Func<TEntity, bool> criteria){
return data.Where(criteria).OrderBy(OrderBy).ThenBy(ThenOrderBy);
}
public Func<TEntity, object> OrderBy { get; set; }
public Func<TEntity, object> ThenOrderBy { get; set; }
}
Live example (you can uncomment the ThenOrderBy line to see it working with/without) : http://rextester.com/YFOB38755
By the way, your existing code could be made much more readable/simple:
public virtual List<TEntity> Get(Expression<Func<TEntity, bool>> criteria)
{
IEnumerable<TEntity> query = _dbSet.Where(criteria);
query = OrderDescending ? query.OrderByDescending(OrderBy) : query.OrderBy(OrderBy)
if (paggedSearch)
{
if(Skip > 0)
query = query.Skip(Skip);
if(Take > 0)
query = query.Take(Take);
}
return query.ToList();
}
There was no need to keep repeating yourself (DRY!)

Implementing a custom QueryProvider with in-memory query

I'm trying to create a wrapper around QueryableBase and INhQueryProvider that would receive a collection in the constructor and query it in-memory instead of going to a database. This is so I can mock the behavior of NHibernate's ToFuture() and properly unit test my classes.
The problem is that I'm facing a stack overflow due to infinite recursion and I'm struggling to find the reason.
Here's my implementation:
public class NHibernateQueryableProxy<T> : QueryableBase<T>, IOrderedQueryable<T>
{
public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data))
{
}
public NHibernateQueryableProxy(IQueryParser queryParser, IQueryExecutor executor) : base(queryParser, executor)
{
}
public NHibernateQueryableProxy(IQueryProvider provider) : base(provider)
{
}
public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression)
{
}
public new IEnumerator<T> GetEnumerator()
{
return Provider.Execute<IEnumerable<T>>(Expression).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
internal class NhQueryProviderProxy<T> : INhQueryProvider
{
private readonly IQueryProvider provider;
public NhQueryProviderProxy(IQueryable<T> data)
{
provider = data.AsQueryable().Provider;
}
public IQueryable CreateQuery(Expression expression)
{
return new NHibernateQueryableProxy<T>(this, expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new NHibernateQueryableProxy<TElement>(this, expression);
}
public object Execute(Expression expression)
{
return provider.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return provider.Execute<TResult>(expression);
}
public object ExecuteFuture(Expression expression)
{
return provider.Execute(expression);
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
Edit: I've kind of figured out the problem. One of the arguments to expression is my custom queryable. When this expression is executed by the provider, it causes an infinite call loop between CreateQuery and Execute. Is it possible to change all the references to my custom queryable to the queryable wrapped by this class?
After a while I decided to give it another try and I guess I've managed to mock it. I didn't test it with real case scenarios but I don't think many tweaks will be necessary. Most of this code is either taken from or based on this tutorial. There are some caveats related to IEnumerable when dealing with those queries.
We need to implement QueryableBase since NHibernate asserts the type when using ToFuture.
public class NHibernateQueryableProxy<T> : QueryableBase<T>
{
public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data))
{
}
public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression)
{
}
}
Now we need to mock a QueryProvider since that's what LINQ queries depend on and it needs to implement INhQueryProvider because ToFuture() also uses it.
public class NhQueryProviderProxy<T> : INhQueryProvider
{
private readonly IQueryable<T> _data;
public NhQueryProviderProxy(IQueryable<T> data)
{
_data = data;
}
// These two CreateQuery methods get called by LINQ extension methods to build up the query
// and by ToFuture to return a queried collection and allow us to apply more filters
public IQueryable CreateQuery(Expression expression)
{
Type elementType = TypeSystem.GetElementType(expression.Type);
return (IQueryable)Activator.CreateInstance(typeof(NHibernateQueryableProxy<>)
.MakeGenericType(elementType), new object[] { this, expression });
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new NHibernateQueryableProxy<TElement>(this, expression);
}
// Those two Execute methods are called by terminal methods like .ToList() and .ToArray()
public object Execute(Expression expression)
{
return ExecuteInMemoryQuery(expression, false);
}
public TResult Execute<TResult>(Expression expression)
{
bool IsEnumerable = typeof(TResult).Name == "IEnumerable`1";
return (TResult)ExecuteInMemoryQuery(expression, IsEnumerable);
}
public object ExecuteFuture(Expression expression)
{
// Here we need to return a NhQueryProviderProxy so we can add more queries
// to the queryable and use another ToFuture if desired
return CreateQuery(expression);
}
private object ExecuteInMemoryQuery(Expression expression, bool isEnumerable)
{
var newExpr = new ExpressionTreeModifier<T>(_data).Visit(expression);
if (isEnumerable)
{
return _data.Provider.CreateQuery(newExpr);
}
return _data.Provider.Execute(newExpr);
}
public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters)
{
throw new NotImplementedException();
}
}
The expression tree visitor will change the type of the query for us:
internal class ExpressionTreeModifier<T> : ExpressionVisitor
{
private IQueryable<T> _queryableData;
internal ExpressionTreeModifier(IQueryable<T> queryableData)
{
_queryableData = queryableData;
}
protected override Expression VisitConstant(ConstantExpression c)
{
// Here the magic happens: the expression types are all NHibernateQueryableProxy,
// so we replace them by the correct ones
if (c.Type == typeof(NHibernateQueryableProxy<T>))
return Expression.Constant(_queryableData);
else
return c;
}
}
And we also need a helper (taken from the tutorial) to get the type being queried:
internal static class TypeSystem
{
internal static Type GetElementType(Type seqType)
{
Type ienum = FindIEnumerable(seqType);
if (ienum == null) return seqType;
return ienum.GetGenericArguments()[0];
}
private static Type FindIEnumerable(Type seqType)
{
if (seqType == null || seqType == typeof(string))
return null;
if (seqType.IsArray)
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
if (seqType.IsGenericType)
{
foreach (Type arg in seqType.GetGenericArguments())
{
Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType))
{
return ienum;
}
}
}
Type[] ifaces = seqType.GetInterfaces();
if (ifaces != null && ifaces.Length > 0)
{
foreach (Type iface in ifaces)
{
Type ienum = FindIEnumerable(iface);
if (ienum != null) return ienum;
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object))
{
return FindIEnumerable(seqType.BaseType);
}
return null;
}
}
To test the above code, I ran the following snippet:
var arr = new NHibernateQueryableProxy<int>(Enumerable.Range(1, 10000).AsQueryable());
var fluentQuery = arr.Where(x => x > 1 && x < 4321443)
.Take(1000)
.Skip(3)
.Union(new[] { 4235, 24543, 52 })
.GroupBy(x => x.ToString().Length)
.ToFuture()
.ToList();
var linqQuery = (from n in arr
where n > 40 && n < 50
select n.ToString())
.ToFuture()
.ToList();
As I said, no complex scenarios were tested but I guess only a few tweaks will be necessary for real-world usages.

passing dynamic expression to order by in code first EF repository

we have written a Generic function to get the records from EF code first in a repository pattern. Rest seems to be ok but when passing an Integer to the dynamic order by , it says Cannot cast System.Int32 to System.Object
the expression is as follows:
Expression<Func<HeadOffice, object>> orderByFunc = o => o.Id;
if (options.sort == "Id")
{
// this is an Integer
orderByFunc = o => o.Id;
}
if (options.sort =="Name")
{
// string
orderByFunc = o => o.Name;
}
if (options.sort == "Code")
{
orderByFunc = o => o.Code;
}
the generic method is as follows:
public virtual IEnumerable<TEntity> GetSorted<TSortedBy>(
Expression<Func<TEntity, object>> order,
int skip, int take,
params Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = dbSet;
foreach (var include in includes)
{
query = dbSet.Include(include);
}
IEnumerable<TEntity> data = query.OrderBy(order).Skip(skip).Take(take).ToList();
return data;
}
if we convert Expression<Func<TEntity, object>> to Expression<Func<TEntity, int>> then it seems to work fine with integer but consequently not with strings
any help appreciated.
Maybe if you change the type of that parameter for this Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy, it could make your life easier:
public virtual IEnumerable<TEntity> GetSorted(Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy,...)
{
IQueryable<TEntity> query = dbSet;
//...
if (orderBy != null)
{
query = orderBy(query);
}
//...
}
This way you can pass an Func like this:
Func<IQueryable<HeadOffice>, IOrderedQueryable<HeadOffice>> orderBy=null;
if (options.sort == "Id")
{
orderBy= query=>query.OrderBy(o => o.Id);
}
//...
Update
Another thing that I notice now is you are not using the TSortedBy generic parameter, so, you could also do this:
public virtual IEnumerable<TEntity> GetSorted<TSortedBy>(Expression<Func<TEntity, TSortedBy>> order,
int skip, int take,
params Expression<Func<TEntity, object>>[] includes)
{
}
But anyway I think is better use the first option and remove that generic parameter.
Create a Sorter class. We also need a property-type-neutral base class:
public class SorterBase<TEntity>
{
public abstract IEnumerable<TEntity> GetSorted( // Note, no order argument here
int skip, int take,
params Expression<Func<TEntity, object>>[] includes);
}
public class Sorter<TEntity, TSortProp> : SorterBase<TEntity>
{
private Expression<Func<TEntity, TSortProp>> _order;
public Sorter(Expression<Func<TEntity, TSortProp>> order)
{
_order = order;
}
public override IEnumerable<TEntity> GetSorted(...)
{
// Use _order here ...
}
}
Now change the sort decision to:
SorterBase<HeadOffice> sorter;
if (options.sort == "Id") {
sorter = new Sorter<HeadOffice, int>(o => o.Id);
} else if (options.sort == "Name") {
sorter = new Sorter<HeadOffice, string>(o => o.Name);
}
...
var result = sorter.GetSorted(skip, take, includes);
One solution is to have two overloaded methods, one takes
Expression<Func<TEntity, int>>
and one takes
Expression<Func<TEntity, string>>
To minimize code duplication, extract common code (for example the query initialization statement and the for loop) to a shared method, and just let the two methods call this shared method and then invoke OrderBy on the result.
If none of the answers work for you and you must have the order expression as:
Expression<Func<TEntity,object>>
then try the following solution:
public class ExpressionHelper : ExpressionVisitor
{
private MemberExpression m_MemberExpression;
public MemberExpression GetPropertyAccessExpression(Expression expression)
{
m_MemberExpression = null;
Visit(expression);
return m_MemberExpression;
}
protected override Expression VisitMember(MemberExpression node)
{
var property = node.Member as PropertyInfo;
if (property != null)
{
m_MemberExpression = node;
}
return base.VisitMember(node);
}
}
public class DataClass<TEntity>
{
private readonly IQueryable<TEntity> m_Queryable;
public DataClass(IQueryable<TEntity> queryable)
{
m_Queryable = queryable;
}
public virtual IEnumerable<TEntity> GetSorted(
Expression<Func<TEntity, object>> order,
int skip, int take,
params Expression<Func<TEntity, object>>[] includes)
{
var property_access_expression = new ExpressionHelper().GetPropertyAccessExpression(order);
if(property_access_expression == null)
throw new Exception("Expression is not a property access expression");
var property_info = (PropertyInfo) property_access_expression.Member;
var covert_method = this.GetType().GetMethod("Convert").MakeGenericMethod(property_info.PropertyType);
var new_expression = covert_method.Invoke(this, new object[] {property_access_expression, order.Parameters });
var get_sorted_method = this.GetType()
.GetMethod("GetSortedSpecific")
.MakeGenericMethod(property_info.PropertyType);
return (IEnumerable<TEntity>)get_sorted_method.Invoke(this, new object[] { new_expression, skip, take, includes });
}
public virtual IEnumerable<TEntity> GetSortedSpecific<TSortedBy>(
Expression<Func<TEntity, TSortedBy>> order,
int skip, int take,
params Expression<Func<TEntity, object>>[] includes)
{
IQueryable<TEntity> query = m_Queryable;
//Here do your include logic and any other logic
IEnumerable<TEntity> data = query.OrderBy(order).Skip(skip).Take(take).ToList();
return data;
}
public Expression<Func<TEntity, TNewKey>> Convert<TNewKey>(
MemberExpression expression, ReadOnlyCollection<ParameterExpression> parameter_expressions )
{
return Expression.Lambda<Func<TEntity, TNewKey>>(expression, parameter_expressions);
}
}
Here is how I tested this:
void Test()
{
Expression<Func<Entity, object>> exp = (x) => x.Text;
List<Entity> entities = new List<Entity>();
entities.Add(new Entity()
{
Id = 1,
Text = "yacoub"
});
entities.Add(new Entity()
{
Id = 2,
Text = "adam"
});
DataClass<Entity> data = new DataClass<Entity>(entities.AsQueryable());
var result = data.GetSorted(exp, 0, 5, null);
}
I tested this with both integer and string properties.
Please note that this only works for simple property access expressions.
You can enhance the GetSorted method to make this work for more complex cases.

Specification pattern with entity framework and using orderby and skip/take

I have picked up a project that uses the specification pattern, a pattern I have not used before, and I had to go and research the pattern. I have noticed it doesn't have OrderBy and Skip/Take functionality, and I can't find anywhere that shows how to implement this with the pattern.
I am struggling to think of how best to add this to the specification pattern. But I have hit issues, like the specification deals with "Expression<Func<T, bool>>" whereas I don't think I can store this along with orderby's etc
Basically there is a class like this:
public class Specification<T> : ISpecification<T>
{
public Expression<Func<T, bool>> Predicate { get; protected set; }
public Specification(Expression<Func<T, bool>> predicate)
{
Predicate = predicate;
}
public Specification<T> And(Specification<T> specification)
{
return new Specification<T>(this.Predicate.And(specification.Predicate));
}
public Specification<T> And(Expression<Func<T, bool>> predicate)
{
return new Specification<T>(this.Predicate.And(predicate));
}
public Specification<T> Or(Specification<T> specification)
{
return new Specification<T>(this.Predicate.Or(specification.Predicate));
}
public Specification<T> Or(Expression<Func<T, bool>> predicate)
{
return new Specification<T>(this.Predicate.Or(predicate));
}
public T SatisfyingItemFrom(IQueryable<T> query)
{
return query.Where(Predicate).SingleOrDefault();
}
public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)
{
return query.Where(Predicate);
}
}
This allows to create a specification, passing in a where clause. It also allows chaining of rules with the "And", "Or". For example:
var spec = new Specification<Wave>(w => w.Id == "1").And(w => w.WaveStartSentOn > DateTime.Now);
How can I add a method for "OrderBy" and "Take"?
As this is existing code, I can't do any changes that would affect existing code, and it would be quite a job to refactor it. So any solution would need to play nicely with what is there.
How about
public class Specification<T> : ISpecification<T>
{
public Expression<Func<T, bool>> Predicate { get; protected set; }
public Func<IQueryable<T>, IOrderedQueryable<T>> Sort {get; protected set; }
public Func<IQueryable<T>, IQueryable<T>> PostProcess {get; protected set;
public Specification<T> OrderBy<TProperty>(Expression<Func<T, TProperty>> property)
{
var newSpecification = new Specification<T>(Predicate) { PostProcess = PostProcess } ;
if(Sort != null) {
newSpecification.Sort = items => Sort(items).ThenBy(property);
} else {
newSpecification.Sort = items => items.OrderBy(property);
}
return newSpecification;
}
public Specification<T> Take(int amount)
{
var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ;
if(PostProcess!= null) {
newSpecification.PostProcess= items => PostProcess(items).Take(amount);
} else {
newSpecification.PostProcess= items => items.Take(amount);
}
return newSpecification;
}
public Specification<T> Skip(int amount)
{
var newSpecification = new Specification<T>(Predicate) { Sort = Sort } ;
if(PostProcess!= null) {
newSpecification.PostProcess= items => PostProcess(items).Skip(amount);
} else {
newSpecification.PostProcess= items => items.Skip(amount);
}
return newSpecification;
}
}
TODO:
similar construction for OrderByDescending
Update your other methods so the "Sort" value and "PostProcess" value is not lost when you call "And", for example
then your Satisfying methods become:
private IQueryable<T> Prepare(IQueryable<T> query)
{
var filtered = query.Where(Predicate);
var sorted = Sort(filtered);
var postProcessed = PostProcess(sorted);
return postProcessed;
}
public T SatisfyingItemFrom(IQueryable<T> query)
{
return Prepare(query).SingleOrDefault();
}
public IQueryable<T> SatisfyingItemsFrom(IQueryable<T> query)
{
return Prepare(query);
}
TODO: check if Sort & PostProcess are not null in the "Prepare" method
Usage:
var spec = new Specification<Wave>(w => w.Id == "1")
.And(w => w.WaveStartSentOn > DateTime.Now)
.OrderBy(w => w.WaveStartSentOn)
.Skip(20)
.Take(5);

Categories

Resources