I am using the Specification pattern to perform db filtering, and avoid doing it on memory (I roughly followed this article). My base specification class is something like this:
public abstract class Specification<T> : ISpecification<T>{
public abstract Expression<Func<T, bool>> FilterExpr();
public bool IsSatisfied(T entity)
{
Func<T, bool> func = this.FilterExpr().Compile();
return func(entity);
}
public Specification<T> And(Specification<T> otherSpec)
{
return new CombinedSpecification<T>(this, otherSpec);
}
}
From this base Specification class, multiple Strongly-typed specifications are derived, which work well on their own. However, the problem arises when trying to combine such specifications and evaluating the CombinedSpecification in my Repository class:
System.InvalidOperationException: The LINQ expression 'DbSet() .Where(c => c.ClientCode == __client_0 && c.Status == "Efective")' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Here is a look at the CombinedSpecification class:
internal class CombinedSpecification<T> : Specification<T>
{
private Specification<T> leftSpec;
private Specification<T> rightSpec;
public CombinedSpecification(Specification<T> aSpec, Specification<T> otherSpec)
{
this.leftSpec = aSpec;
this.rightSpec = otherSpec;
}
public override Expression<Func<T, bool>> FilterExpr()
{
Expression<Func<T, bool>> firstExpr = this.leftSpec.FilterExpr();
Expression<Func<T, bool>> secondExpr = this.rightSpec.FilterExpr();
BinaryExpression combined = Expression.AndAlso(firstExpr.Body, secondExpr.Body);
return Expression.Lambda<Func<T, bool>>(combined, firstExpr.Parameters.Single());
}
}
Just to be clear, filtering by a single specification works just fine, yet the LinQ expression seems impossible to translate when combining them (which seems weird to me since I'm not using any methods that are unsupported by SQL AFAIK). I avoided showing the Repository class to reduce this question's volume, but here is the relevant lines of my Find() method anyways:
public IEnumerable<TEntity> Find(Specification<TEntity> spec)
{
IQueryable<TEntity> result = this.dbSet;
if (spec != null)
{
result = result.Where(spec.FilterExpr());
}
return result.ToList();
}
Thanks in advance for the help, I hope my first question was clearly stated!