I have a repository->service architecture that uses a LINQ expression to filter many data. Repositories work with entities and Services with the DTOs. At the moment, I use Automapper to map entity to dto and vice-versa. In the repository, I have a method that accepts an Expression> LINQ expression. This method is called by the service using an Expression> LINQ expression. So, I've used Automapper to map the service's expression to the repository's expression.
The project builds successfully but I have an error at runtime.
This is the method that throws the error in the service:
public IQueryable<TDto> GetBy(Expression<Func<TDto, bool>> predicate)
=> this.Repository.GetBy(Mapper.Map<Expression<Func<TEntity, bool>>>(predicate))
.ProjectTo<TDto>(Mapper.ConfigurationProvider);
And this is the method called in the repository:
public IQueryable<TEntity> GetBy(Expression<Func<TEntity, bool>> predicate)
=> this.Context.Set<TEntity>().AsNoTracking().Where(predicate);
The mapping between entity and dto is the following:
CreateMap<TEntity, TDto>();
CreateMap<TDto, TEntity>();
At runtime I'm receiving this error:
AutoMapper.AutoMapperMappingException: 'Missing type map configuration or unsupported mapping.'
Also, i've tryed to map explicitly the expressions:
CreateMap<Expression<Func<TEntity, bool>>, Expression<Func<TDto, bool>> >();
CreateMap<Expression<Func<TDto, bool>>, Expression<Func<TEntity, bool>>>();
But i've received this error:
System.InvalidOperationException: 'Code supposed to be unreachable'
Does anyone have a possible solution?
I have never seen capability to automaticaly transform lambdas.
What you should do instead is to try use Project method.
For example:
var predicate = new Func<Dto, bool>(d => d.Id == 2);
var query = mapper.ProjectTo<Dto>(entities, null).Where(predicate);
Query will be equivalent of:
var query = entities
.Select(e => new Dto { Id = e.Id, [...] }) // mapping created using map registered in AutoMapper
.Where(d => d.Id == 2)
Another thing you can do is to map expression by yourself. As a starpoint you can use query produced by Project method:
var query = mapper.ProjectTo<Dto>(entities, null);
var lambda = (LambdaExpression)((UnaryExpression)((MethodCallExpression) query.Expression).Arguments[1]).Operand;
var body = (MemberInitExpression)lambda.Body;
var bindings = body.Bindings;
As a result of the code above you will have array of bindings like dto.Id = entity.Id.
Having that it should be pretty easy to write custom mapper:
public static class MapperExtensions
{
public static Expression<Func<TEntity, bool>> ConvertPredicate<TDto, TEntity>(this Mapper mapper, Expression<Func<TDto, bool>> predicate)
{
return (Expression<Func<TEntity, bool>>)new PredicateVisitor<TDto, TEntity>(mapper).Visit(predicate);
}
public class PredicateVisitor<TDto, TEntity> : ExpressionVisitor
{
private readonly ParameterExpression _entityParameter;
private readonly MemberAssignment[] _bindings;
public PredicateVisitor(Mapper mapper)
{
IQueryable<TDto> mockQuery = mapper.ProjectTo<TDto>(new TEntity[0].AsQueryable(), null);
LambdaExpression lambdaExpression = (LambdaExpression)((UnaryExpression)((MethodCallExpression) mockQuery.Expression).Arguments[1]).Operand;
this._bindings = ((MemberInitExpression)lambdaExpression.Body).Bindings.Cast<MemberAssignment>().ToArray();
this._entityParameter = Expression.Parameter(typeof(TEntity));
}
// This is required to modify type parameters
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(
base.Visit(node.Body),
node.Parameters.Select(p => (ParameterExpression)base.Visit(p)).ToArray()
);
}
// Do member mapping
protected override Expression VisitMember(MemberExpression node)
{
MemberInfo member = node.Member;
MemberAssignment binding = this._bindings.FirstOrDefault(b => b.Member == member);
if (binding != null)
{
return base.Visit(binding.Expression);
}
return base.VisitMember(node);
}
// Replace parameters reference
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TDto))
{
return this._entityParameter;
}
if (node.Type == typeof(TEntity))
{
return this._entityParameter;
}
return base.VisitParameter(node);
}
}
}
Related
For EF6, I had a method in my generic repository that I exposed to all service layers in order to retrieve entities from the database with any nested properties as needed:
public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
{
var queryable = set.AsQueryable();
return includeProperties.Aggregate(queryable, (current, includeProperty) => current.Include(includeProperty));
}
This way, I could use the method in the following way:
var data = repo.OldMethod(x => x.Papers, => x.People.Select(y => y.Addresses)).ToList();
In EF6, this would load the Papers navigation property, the People navigation property, and the Addresses navigation property on each person. This, as expected, throws an exception in EFCore. Because of the switch to Include-->ThenInclude method in EFCore, I'm not quite sure how to easily replicate this at my service layer which I'd like to not require any information about EntityFramework.
This has been asked many times since the initial release of EF Core. Earlier prerelease versions of EF Core even were supporting it, but then it has been removed from EF Core code (I guess in order to promote the new Include / ThenInclude pattern).
While Include / ThenInclude pattern looks more clear (besides the current Intellisense issues), it has one major drawback - requires access to EntityFrameworkQueryableExtensions, thus reference to Microsoft.EntityFrameworkCore assembly. While paramsExpression>` pattern has no such requirement.
The good thing is the one can relatively easily add that functionality. The EF6 source code is publicly available on GitHub, and from there we can see that it uses a method called TryParsePath to build dot separated string path which then is passed to the string overload of Include method.
The same can be applied in EF Core. We can probably use the EF6 code, but I'm going to provide my own version. It can be easily be seen that the supported constructs are member accessors or calls to method called Select with 2 arguments, the second being LambdaExpression.
Following is my interpretation of the above, encapsulated in two custom extension methods:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Microsoft.EntityFrameworkCore
{
public static class IncludeExtensions
{
public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<string> includePaths) where T : class
=> includePaths.Aggregate(source, (query, path) => query.Include(path));
public static IQueryable<T> Include<T>(this IQueryable<T> source, IEnumerable<Expression<Func<T, object>>> includePaths) where T : class
=> source.Include(includePaths.Select(e => GetIncludePath(e?.Body)));
static string GetIncludePath(Expression source, bool allowParameter = false)
{
if (allowParameter && source is ParameterExpression)
return null; // ok
if (source is MemberExpression member)
return CombinePaths(GetIncludePath(member.Expression, true), member.Member.Name);
if (source is MethodCallExpression call && call.Method.Name == "Select"
&& call.Arguments.Count == 2 && call.Arguments[1] is LambdaExpression selector)
return CombinePaths(GetIncludePath(call.Arguments[0]), GetIncludePath(selector.Body));
throw new Exception("Invalid Include path.");
}
static string CombinePaths(string path1, string path2)
=> path1 != null ? path1 + "." + path2 : path2;
}
}
The first is simply helper for calling multiple string includes (taken from my answer to Entity Framework Core 2.0.1 Eager Loading on all nested related entities). The second is the method in question, which converts the expressions to strings and call the first. The main work is done by GetIncludePath private method which recursively processes the expression based on the aforementioned rules, plus one additional rule - when navigating bottom up, it should end with lambda parameter.
Now the implementation of the method is question is simple as that:
public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
=> set.Include(includeProperties);
When I first started using EFCore (switching from EF6), I built these extension methods to translate the "old" way of including x => x.People.Select(y => y.Addresses) to strings like "People.Addresses", which are supported by EFCore too;
public static class Extensions
{
private class ReferencedPropertyFinder : ExpressionVisitor
{
private readonly Type _ownerType;
private readonly List<PropertyInfo> _properties = new List<PropertyInfo>();
private Expression _parameterExpression;
private int _currentPosition = 0;
public ReferencedPropertyFinder(Type ownerType)
{
_ownerType = ownerType;
}
public IReadOnlyList<PropertyInfo> Properties
{
get { return _properties; }
}
protected override Expression VisitMember(MemberExpression node)
{
var propertyInfo = node.Member as PropertyInfo;
if (propertyInfo != null) {
var currentParameter = GetParameter(node);
if (_parameterExpression == currentParameter) {
_properties.Insert(_currentPosition, propertyInfo);
} else {
_properties.Add(propertyInfo);
_parameterExpression = currentParameter;
_currentPosition = _properties.Count() - 1;
}
}
return base.VisitMember(node);
}
private ParameterExpression GetParameter(MemberExpression node)
{
if (node.Expression is ParameterExpression) {
return (ParameterExpression)node.Expression;
} else {
return GetParameter((MemberExpression)node.Expression);
}
}
}
private static IReadOnlyList<PropertyInfo> GetReferencedProperties<T, U>(this Expression<Func<T, U>> expression)
{
var v = new ReferencedPropertyFinder(typeof(T));
v.Visit(expression);
return v.Properties;
}
public static string ToPropertyPath<T>(this Expression<Func<T, object>> expression)
{
var properties = expression.GetReferencedProperties();
var path = string.Join(".", properties.Select(x => x.Name));
return path;
}
}
Incorporating these in your code, you could say something like:
public IQueryable<T> OldMethod(params Expression<Func<T, object>>[] includeProperties)
{
var queryable = set.AsQueryable();
return includeProperties.Aggregate(queryable, (current, includeProperty) =>
current.Include(includeProperty.ToPropertyPath()));
}
I'd like to move predicate building logic into a base class and am getting the error “The LINQ expression node type 'Invoke' is not supported in LINQ to Entities”. I want to be able to concatenate or conditionally chain the expressions in the predicate.
I want to be able to pass in the part of the predicate that is unique to the caller which is the property names used (the GetFilterPredicate will become a generic routine which will operate on types that will have different property names holding the relevant values).
protected Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Func<MyEntity, DateTime?> terminationDate,
Func<MyEntity, string> entityID,
Func<MyEntity, string> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
if (pagingParameters.Active == true)
{
validFilter = x => terminationDate(x) > DateTime.Now;
}
///more conditions added here
return validFilter;
}
protected List<MyEntity> Query(IQueryable<MyEntity> source)
{
var filters = GetFilterPredicate(
new PagingParameters() { Active = true }
, i => i.TerminationDate
, i => i.EntityID
, i => i.Name
);
return source.Where(filters).AsNoTracking().ToList<MyEntity>();
}
You can't construct your new expression using the delegate terminationDate you need to change terminationDate to be an Expression and use it to build a new expression manually.
protected static Expression<Func<MyEntity, bool>> GetFilterPredicate(
PagingParameters pagingParameters,
Expression<Func<MyEntity, DateTime?>> terminationDate,
Expression<Func<MyEntity, string>> entityID,
Expression<Func<MyEntity, string>> name
)
{
Expression<Func<MyEntity, bool>> validFilter = x => true;
// We need to replace the parameter for all expressions with
// a common single parameter. I used the parameter for the default
// filter but a new Parameter expression would have worked as well.
// If you don't do this you will get an error (unbound parameter or something like that ) because the parameter
// used in the expressions (terminationDate, entityID) will be
// different then the parameter used for the new validFilter expression
var parameterReplacer = new ReplaceVisitor
{
NewParameter = validFilter.Parameters.First()
};
if (pagingParameters.Active == true)
{
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.GreaterThan
(
parameterReplacer.Visit(terminationDate.Body),
Expression.Convert(Expression.Property(null, typeof(DateTime), "Now"), typeof(DateTime?))
),
parameterReplacer.NewParameter
);
}
// existing filter && x.EntityId != "A"
validFilter = Expression.Lambda<Func<MyEntity, bool>>(
Expression.And(
validFilter.Body,
Expression.NotEqual
(
parameterReplacer.Visit(entityID.Body),
Expression.Constant("A")
)
),
parameterReplacer.NewParameter
);
return validFilter;
}
/// <summary>
/// Simple Parameter Replacer, will replace the any parameter with the new
/// parameter. You will need to refine this if your expressions have nested
/// lambda, in that you will need to only replace the top most lambda
/// parameter, but for simple expressions it will work fine.
/// </summary>
class ReplaceVisitor : ExpressionVisitor
{
public ParameterExpression NewParameter { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return this.NewParameter;
}
}
You can do this manually as Titian answer describes, but easier way is to use LinqKit. First install LinqKit nuget package and add using LinqKit;, then:
// change terminaionDate to Expression<Func<MyEntity, DateTime>>
static Expression<Func<MyEntity, bool>> GetFilterPredicate(Expression<Func<MyEntity, DateTime>> terminationDate) {
// note Invoke
Expression<Func<Error, bool>> filter = c => terminationDate.Invoke(c) <= DateTime.Now;
// note Expand
return filter.Expand();
}
You can read about Invoke and Expand at the link provided above.
I am creating an IQueryable that I want to use for a query passed to entity framework. My repository does not expose queryable.
var query = new List<Entity>().AsQueryable().Where(x => x.Property == "argument");
I have a method on my repository that will take in an IQueryable.
How do I query my DbSet with the same queryable? I am trying to extract the expression from the queryable to build a new expression for the dbset. Here is what I have so far but it does not work:
public IDbSet<TEntity> DbSet { get; set; }
public IEnumerable<TEntity> Find(IQueryable<TEntity> queryable)
{
var parameter = Expression.Parameter(typeof (TEntity));
var body = queryable.Expression;
var lambda = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var result = DbSet.Where(lambda);
return null;
}
The code fails when I try and create the lambda with the following error:
Expression of type 'System.Linq.IQueryable`1[MyTEntity]' cannot be used for return type 'System.Boolean'
I'm clearly not building the expression correctly, what am I missing? Is there an easier way to do what I'm trying to accomplish?
Also I've seen some examples that show an Expression should have a parameters property. But no matter what type of expression type I cast to, and this one is ConstantExpression, I don't see a parameters property from the IQueryable.Expression.
In your case, queryable.Expression represents the whole expression Queryable.Where(constantList, x => x.Property == "argument"). If you want just the Where() lambda, you need to extract it. To do that, you could use code like this:
public IEnumerable<TEntity> Find<TEntity>(IQueryable<TEntity> queryable)
{
var methodCall = queryable.Expression as MethodCallExpression;
Func<IQueryable<TEntity>, Expression<Func<TEntity,Boolean>>, IQueryable<TEntity>> whereDelegate = Queryable.Where;
if (methodCall.Method == whereDelegate.Method
&& methodCall.Arguments[0] is ConstantExpression)
{
var whereLambdaQuote = (UnaryExpression)methodCall.Arguments[1];
var whereLambda = (Expression<Func<TEntity, bool>>)whereLambdaQuote.Operand;
var result = DbSet.Where(whereLambda);
}
return null;
}
I have an expression like this
(a,b) => a.Id == b.Id
I would like to use it in LINQ to Entities query
T GetSingle(IRepository<T> repository, Func<T,T,bool> predicate, T entity)
{
return repository.GetAll().Single(e => predicate(e, entity))
}
but this results the exception: LINQ expression node type 'Invoke' is not supported in LINQ to Entities
As I understand I can use Expressions to construct a valide predicate for LINQ2SQL, so my expression
(a,b) => a.Id == b.Id and instance of entity with Id = 5 can result a new expression (a) => a.Id == 5.
And the last expression will be fine for LINQ to Entities.
I found and read this articles
Replace parameter in lambda expression
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
but still has no clue how to solve my task
So, how do I convert given expression dynamically?
Why don't you just change your method to be:
T GetSingle(IRepository<T> repository, Expression<Func<TSource, Boolean>> predicate)
{
return repository.GetAll().Single(predicate);
}
so instead of this:
GetSingle(myRepository, (a,b) => a.Id == b.Id, myEntity);
you should be able to do this:
GetSingle(myRepository, a => a.Id == myEntity.Id);
I haven't tested it with Linq2SQL, but it seems to me that you should be able to do this with an expression visitor and compiling the expression to write the value of your parameter into the expression you've supplied (assuming you switch over to using Expression<Func<T, T, bool>> instead of Func<T, T, bool>) and creating a wrapper that itself invokes Enumerable.Single on the result from the GetAll
The visitor (for specifically the example you've given would look like this)
public class VariableSubstitutionVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly ConstantExpression _constant;
public VariableSubstitutionVisitor(ParameterExpression parameter, ConstantExpression constant)
{
_parameter = parameter;
_constant = constant;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
{
return _constant;
}
return node;
}
}
Now, we'd adjust the GetSingle method to look like this:
public T GetSingle(IRepository<T> repository, Expression<Func<T, T, bool>> predicate, T entity)
{
//Create a new representation of predicate that will take just one parameter and capture entity
//Get just the body of the supplied expression
var body = predicate.Body;
//Make a new visitor to replace the second parameter with the supplied value
var substitutionVisitor = new VariableSubstitutionVisitor(predicate.Parameters[1], Expression.Constant(entity, typeof(T)));
//Create an expression that represents the predicate with the second parameter replaced with the supplied entity
var visitedBody = substitutionVisitor.Visit(body).Reduce();
//Make the new expression into something that could be a Func<T, bool>
var newBody = Expression.Lambda<Func<T, bool>>(visitedBody, predicate.Parameters[0]);
//Now, create something that will call Enumerable.Single on the result of GetAll from the repository, supplying the new predicate
//Make a place to hold the result of GetAll
var resultsParameter = Expression.Parameter(typeof (IEnumerable<T>));
//Make an expression that calls the Single extension method
var singleExpression = Expression.Call(((Func<IEnumerable<T>, Func<T, bool>, T>)Enumerable.Single).Method, resultsParameter, newBody);
//Make a Func<IEnumerable<T>, T> that return the result of the call of Single on the results of the GetAll method
var compiled = Expression.Lambda<Func<IEnumerable<T>, T>>(singleExpression, resultsParameter).Compile();
//Call GetAll, letting the new method that we've got run the supplied predicate without having to run an Invoke type expression
return compiled(repository.GetAll());
}
The trick, of course, is getting that to perform well.
Here is code I just wrote in the context of Linq2Sql:
public static CampaignEntity GetOrCreate(int pid, SynchDBDataContext db)
{
CampaignEntity ce = (from c in db.CampaignEntities
where c.pid == pid
select c).FirstOrDefault();
if (ce == null)
{
ce = new CampaignEntity();
ce.pid = pid;
db.CampaignEntities.InsertOnSubmit(ce);
}
return ce;
}
The things to make a generic routine would be:
- the entity type
- the type of the primary key column
- the value of the primary key
What I've seen done before is something like this:
public static TEntity GetOrCreate<TEntity, TPKey>(TPKey pid, SyncDBCataContext db, Func<int, TEntity> create){...}
The Func would be the method that creates the entity, so you could inline it when you call this function or you could take this out and hard-code how an entity is created like you have done in your question.
Here is a method that will get by id:
public T GetById<TEntity, TPKey>(TPKey id, DataContext context) where TEntity : class
{
MetaTable metaTable = context.Mapping.GetTable(typeof(TEntity));
MetaDataMember primaryKeyMetaDataMember = metaTable.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);
return context.GetTable<TEntity>().SingleOrDefault(GetEqualityLambdaExpression<TEntity>(primaryKeyMetaDataMember.Name, id));
}
Here is a method that will create the expression for the filter needed by the get by id method:
public Expression<Func<T, bool>> GetEqualityLambdaExpression<T>(string fieldName, object constantValue)
{
ParameterExpression param = Expression.Parameter(typeof(T), "e");
Expression<Func<T, bool>> expression = Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.Property(param, fieldName),
Expression.Constant(constantValue)),
new ParameterExpression[] { param });
return expression;
}
As you can see there is some use of reflection. If performance is a big concern, you can implement some sort of caching to reduce the overhead in future calls.