Conditionally call Linq extension method - c#

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

Related

Multiple Includes() in EF Core

I have an extension method that lets you generically include data in EF:
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
where T : class
{
if (includes != null)
{
query = includes.Aggregate(query, (current, include) => current.Include(include));
}
return query;
}
This allows me to have methods in my repository like this:
public Patient GetById(int id, params Expression<Func<Patient, object>>[] includes)
{
return context.Patients
.IncludeMultiple(includes)
.FirstOrDefault(x => x.PatientId == id);
}
I believe the extension method worked before EF Core, but now including "children" is done like this:
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author);
Is there a way to alter my generic extension method to support EF Core's new ThenInclude() practice?
As said in comments by other, you can take EF6 code to parse your expressions and apply the relevant Include/ThenInclude calls. It does not look that hard after all, but as this was not my idea, I would rather not put an answer with the code for it.
You may instead change your pattern for exposing some interface allowing you to specify your includes from the caller without letting it accessing the underlying queryable.
This would result in something like:
using YourProject.ExtensionNamespace;
// ...
patientRepository.GetById(0, ip => ip
.Include(p => p.Addresses)
.ThenInclude(a=> a.Country));
The using on namespace must match the namespace name containing the extension methods defined in the last code block.
GetById would be now:
public static Patient GetById(int id,
Func<IIncludable<Patient>, IIncludable> includes)
{
return context.Patients
.IncludeMultiple(includes)
.FirstOrDefault(x => x.EndDayID == id);
}
The extension method IncludeMultiple:
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
Func<IIncludable<T>, IIncludable> includes)
where T : class
{
if (includes == null)
return query;
var includable = (Includable<T>)includes(new Includable<T>(query));
return includable.Input;
}
Includable classes & interfaces, which are simple "placeholders" on which additional extensions methods will do the work of mimicking EF Include and ThenInclude methods:
public interface IIncludable { }
public interface IIncludable<out TEntity> : IIncludable { }
public interface IIncludable<out TEntity, out TProperty> : IIncludable<TEntity> { }
internal class Includable<TEntity> : IIncludable<TEntity> where TEntity : class
{
internal IQueryable<TEntity> Input { get; }
internal Includable(IQueryable<TEntity> queryable)
{
// C# 7 syntax, just rewrite it "old style" if you do not have Visual Studio 2017
Input = queryable ?? throw new ArgumentNullException(nameof(queryable));
}
}
internal class Includable<TEntity, TProperty> :
Includable<TEntity>, IIncludable<TEntity, TProperty>
where TEntity : class
{
internal IIncludableQueryable<TEntity, TProperty> IncludableInput { get; }
internal Includable(IIncludableQueryable<TEntity, TProperty> queryable) :
base(queryable)
{
IncludableInput = queryable;
}
}
IIncludable extension methods:
using Microsoft.EntityFrameworkCore;
// others using ommitted
namespace YourProject.ExtensionNamespace
{
public static class IncludableExtensions
{
public static IIncludable<TEntity, TProperty> Include<TEntity, TProperty>(
this IIncludable<TEntity> includes,
Expression<Func<TEntity, TProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity>)includes).Input
.Include(propertySelector);
return new Includable<TEntity, TProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, TProperty> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, TProperty>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
public static IIncludable<TEntity, TOtherProperty>
ThenInclude<TEntity, TOtherProperty, TProperty>(
this IIncludable<TEntity, IEnumerable<TProperty>> includes,
Expression<Func<TProperty, TOtherProperty>> propertySelector)
where TEntity : class
{
var result = ((Includable<TEntity, IEnumerable<TProperty>>)includes)
.IncludableInput.ThenInclude(propertySelector);
return new Includable<TEntity, TOtherProperty>(result);
}
}
}
IIncludable<TEntity, TProperty> is almost like IIncludableQueryable<TEntity, TProperty> from EF, but it does not extend IQueryable and does not allow reshaping the query.
Of course if the caller is in the same assembly, it can still cast the IIncludable to Includable and start fiddling with the queryable. But well, if someone wants to get it wrong, there is no way we would prevent him doing so (reflection allows anything). What does matter is the exposed contract.
Now if you do not care about exposing IQueryable to the caller (which I doubt), obviously just change your params argument for a Func<Queryable<T>, Queryable<T>> addIncludes argument, and avoid coding all those things above.
And the best for the end: I have not tested this, I do not use Entity Framework currently!
For posterity, another less eloquent, but simpler solution that makes use of the Include() overload that uses navigationPropertyPath:
public static class BlogIncludes
{
public const string Posts = "Posts";
public const string Author = "Posts.Author";
}
internal static class DataAccessExtensions
{
internal static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query,
params string[] includes) where T : class
{
if (includes != null)
{
query = includes.Aggregate(query, (current, include) => current.Include(include));
}
return query;
}
}
public Blog GetById(int ID, params string[] includes)
{
var blog = context.Blogs
.Where(x => x.BlogId == id)
.IncludeMultiple(includes)
.FirstOrDefault();
return blog;
}
And the repository call is:
var blog = blogRepository.GetById(id, BlogIncludes.Posts, BlogIncludes.Author);
You can do something like this:
public Patient GetById(int id, Func<IQueryable<Patient>, IIncludableQueryable<Patient, object>> includes = null)
{
IQueryable<Patient> queryable = context.Patients;
if (includes != null)
{
queryable = includes(queryable);
}
return queryable.FirstOrDefault(x => x.PatientId == id);
}
var patient = GetById(1, includes: source => source.Include(x => x.Relationship1).ThenInclude(x => x.Relationship2));
I made this method to do the dynamic includes. This way the "Select" command can be used in lambda to include just as it was in the past.
The call works like this:
repository.IncludeQuery(query, a => a.First.Second.Select(b => b.Third), a => a.Fourth);
private IQueryable<TCall> IncludeQuery<TCall>(
params Expression<Func<TCall, object>>[] includeProperties) where TCall : class
{
IQueryable<TCall> query;
query = context.Set<TCall>();
foreach (var property in includeProperties)
{
if (!(property.Body is MethodCallExpression))
query = query.Include(property);
else
{
var expression = property.Body as MethodCallExpression;
var include = GenerateInclude(expression);
query = query.Include(include);
}
}
return query;
}
private string GenerateInclude(MethodCallExpression expression)
{
var result = default(string);
foreach (var argument in expression.Arguments)
{
if (argument is MethodCallExpression)
result += GenerateInclude(argument as MethodCallExpression) + ".";
else if (argument is MemberExpression)
result += ((MemberExpression)argument).Member.Name + ".";
else if (argument is LambdaExpression)
result += ((MemberExpression)(argument as LambdaExpression).Body).Member.Name + ".";
}
return result.TrimEnd('.');
}
Ofcourse there is,
you could traverse the Expression tree of original params, and any nested includes, add them as
.Include(entity => entity.NavigationProperty)
.ThenInclude(navigationProperty.NestedNavigationProperty)
But its not trivial, but definitely very doable, please share if you do, as it can defintiely be reused!
public Task<List<TEntity>> GetAll()
{
var query = _Db.Set<TEntity>().AsQueryable();
foreach (var property in _Db.Model.FindEntityType(typeof(TEntity)).GetNavigations())
query = query.Include(property.Name);
return query.ToListAsync();
}
I adhere the simpler solution that makes use of the Include() overload that uses string navigationPropertyPath. The simplest that I can write is this extension method below.
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace MGame.Data.Helpers
{
public static class IncludeBuilder
{
public static IQueryable<TSource> Include<TSource>(this IQueryable<TSource> queryable, params string[] navigations) where TSource : class
{
if (navigations == null || navigations.Length == 0) return queryable;
return navigations.Aggregate(queryable, EntityFrameworkQueryableExtensions.Include); // EntityFrameworkQueryableExtensions.Include method requires the constraint where TSource : class
}
}
}
public TEntity GetByIdLoadFull(string id, List<string> navigatonProoperties)
{
if (id.isNullOrEmpty())
{
return null;
}
IQueryable<TEntity> query = dbSet;
if (navigationProperties != null)
{
foreach (var navigationProperty in navigationProperties)
{
query = query.Include(navigationProperty.Name);
}
}
return query.SingleOrDefault(x => x.Id == id);
}
Here is a much simpler solution, idea is to cast the dbset to iqueryable and then recursively include properties
I handle it with like that;
I have Article entity. It includes ArticleCategory entity. And also ArticleCategory entity includes Category entity.
So: Article -> ArticleCategory -> Category
In My Generic Repository;
public virtual IQueryable<T> GetIncluded(params Func<IQueryable<T>, IIncludableQueryable<T, object>>[] include)
{
IQueryable<T> query = Entities; // <- this equals = protected virtual DbSet<T> Entities => _entities ?? (_entities = _context.Set<T>());
if (include is not null)
{
foreach (var i in include)
{
query = i(query);
}
}
return query;
}
And I can use it like that;
var query = _articleReadRepository.GetIncluded(
i => i.Include(s => s.ArticleCategories).ThenInclude(s => s.Category),
i => i.Include(s => s.User)
);

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

How to check if property exists in Type in an extension method?

I frequently have code like this:
var stRecs = db.<someTable>
.Where(a => a.DepID == depID)
to select a single record, however if depID == 0 I would like to get back all records.
I was thinking about creating an extension method "WhereDepID_OrAll", like
public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source)
where T: // is what?
{
if(depID > 0) { return source.Where(a => a.depID == depID); }
else return source.Where(a => a);
}
Now my basic question is:
I have several tables with depID - how do I set the Where T: ?
How would the method determine, whether the table has depID?
A better approach to the underlying problem?
At first glance, the reaction would be : create an interface
public interface ObjectWithDepartmentInterface {
int depID;
}
make all the entities using this depId implementing this interface, and use
where T : ObjectWithDepartmentInterface
but linq to entities doesn't accept properties from interfaces in query... See for example : Expression generated based on interface
So the only way would to make your entities with a depId inheriting from a common entity (probably abstract) with a depId property.
And use this abstract entity as the
where T:
An easier (but uglier way) could be to not add a constraint on T, build the predicate in the method, and throw an exception in bad cases.
if (typeof(T).GetProperty("depId") == null)
throw InvalidOperationException (string.Format("{0}" doesn't have a depId property, typeof(T).Name))
EDIT
But maybe it's not a problem of depId as a common property
Then
public static IQueryable<T> WhereExistsOrAll<T>(this IQueryable<T> source, string propertyName, int value)
where T: // is what?
{
if (value == 0)
return source;
var parameter = Expression.Parameter(typeof(T), "m");
Expression member = parameter;
member = Expression.Property(member, propertyName);
member = Expression.Equals(member, Expression.Constant(value));
var lambda = Expression.Lambda<Func<T, bool>>(member, new[]{parameter});
return source.Where(lambda);
}
usage
var stRecs = db.<someTable>.WhereExistsOrAll("depId", depId);
EDIT 2
Another way would be to parse the Predicate to get the "constant" value
something like that
public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
var expression = predicate.Body as BinaryExpression;
var rightPart = expression.Right as MemberExpression;
var value = GetValue(rightPart);
var test = value.ToString();
int val;
if (Int32.TryParse(value.ToString(), out val))
{
if (val != 0)
return queryable.Where(predicate);
}
return queryable;
}
private static object GetValue(MemberExpression member)
{
var objectMember = Expression.Convert(member, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
}
usage
var stRecs = db.<someTable>.GetAllOrRestrict(m => m.depID == depId);
I know it's not particularly fashionable, but isn't this exactly what the Query Builder methods in Entity Framework are for?
var stRecs = db.<someTable>
.Where("it.DepID == #depID OR #depID = 0",
new ObjectParameter("depID", depID));
This works on any someTable such that it has a column named DepID. It can of course be made an extension method:
public static ObjectQuery<T> WhereIdEqualOrAll<T>(this ObjectQuery<T> q, int depID)
where T : class
{
return q.Where("it.DepID = #id OR #id = 0", new ObjectParameter("id", id));
}
to be invoked thus:
var stRecs = db.<someTable>.WhereIdEqualOrAll(depID);
Use an interface:
public interface IInterface
{
int depId;
}
Which will force T to inherit from IInterface and implement depId.
Then you can add it to the extension:
public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source) where T: IInterface
{
}
Use an interface, then build an expression tree manually, referencing the actual class.
Raphaels Edit 2 did the job!
small edition:
How could I include NULL-Values if a DepID is present?
I like to return all Departments with ID == x OR ID == NULL
Maybe with an additional bool includeNullValues)
public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable,
Expression<Func<T, bool>> predicate,
bool includeNullValues)
{
var expression = predicate.Body as BinaryExpression;
var rightPart = expression.Right as MemberExpression;
var value = GetValue(rightPart);
var test = value.ToString();
int val;
if (Int32.TryParse(value.ToString(), out val))
{
if (val != 0)
{
if (includeNullValues)
{
var parameter = Expression.Parameter(typeof(T),"m");
return queryable.Where(predicate) <====HOW to " || depID == null) ???
}
else
{
return queryable.Where(predicate);
}
}
}
return queryable;
}

List of Expression<Func<T, TProperty>>

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

Categories

Resources