Dynamically generated lookup key for IQueryable - c#

I have the following simple cache class that I want to use to cache items frequently read from the database.
public class Cache<TKey, TElement>
where TElement : class
{
private Dictionary<TKey, TElement> cache = new Dictionary<TKey, TElement>();
private Func<TElement, TKey> dbKey;
public Cache(Func<TElement, TKey> dbKey)
{
this.dbKey = dbKey;
}
public TElement Get(TKey key, DataContext db)
{
if (cache.ContainsKey(key)) return cache[key];
// This line throws exception. See below
var value = db.GetTable<TElement>().SingleOrDefault(x => dbKey(x).Equals(key));
if (value != null) cache[key] = value;
return value;
}
}
The usage looks like this:
var db = new DataContext();
var userCache = new Cache<string, User>(u => u.Username);
userCache.Get("dave", db);
However, the indicated line throws a NotSupportedException with the message "Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL." I understand why the exception is being thrown, but I'm not sure how to resolve it.
The cache is intended to operate in front of a number of different db tables, with different columns being used for the lookup key. My thought was to pass in a lambda to specify the lookup column, as you can see in the code. This works for IEnumerable, but not IQueryable.
Is there another way to achieve this in LINQ to SQL?

You have to use Expression Tree, not delegates and lambdas.
Change your constructor parameter type and field type to Expression<Func<TElement, TKey>>:
private Expression<Func<TElement, TKey>> dbKey;
public Cache(Expression<Func<TElement, TKey>> dbKey)
{
this.dbKey = dbKey;
}
Thanks to compiler which automatically generates Expression Tree from lambda expression at compile time, you can still call it like before:
var userCache = new Cache<string, User>(u => u.Username);
Add private helper method to combine key selector expression with equality check:
private Expression<Func<TElement, bool>> GetConditionExpression(TKey key)
{
var param = Expression.Parameter(typeof(TElement));
return
Expression.Lambda<Func<TElement, bool>>(
Expression.Equal(
Expression.Invoke(
dbKey,
param
),
Expression.Constant(key)
),
param
);
}
Call that method to get proper Expression for SingleOrDefault:
var value = db.GetTable<TElement>().SingleOrDefault(GetConditionExpression(key));
I can't test it right now with real DB, but should work. If not, at least should point you into right dirrection.

Related

How to pass Func to a class which encapsulates the Expression based on that Func?

I have class DropdownFilter which has:
private readonly Func<TEntity, string> fieldWhichMustEqualValue;
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
return filteredEntityCollection.Where(entity => this.fieldWhichMustEqualValue(entity) == value);
}
I use it as:
IQueryable<Invoice> entityCollectionToFilterAndOrder = ...
var dropdownFilter = new DropdownFilter<Invoice>(invoice => invoice.SomeProperty);
entityCollectionToFilterAndOrder = dropdownFilter.Filter(entityCollectionToFilterAndOrder, "bla bla bla");
which gives me
The LINQ expression node type 'Invoke' is not supported in LINQ to
Entities.
I understand the issue is that I am essentially asking for SQL equivalent of Invoke, which of course is wrong.
How should I rewrite the code? I understand that it needs to be an expression. My goal is the consumer of the DropDownFilter to just specify a property of TEntity, without providing the expression. i.e. the expression has to be encapsulated into the filter.
I have tried:
Expression<Func<TEntity, string>> expr = mc => this.fieldWhichMustEqualValue(mc);
Expression le = Expression.Equal(expr.Body, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TEntity, bool>>(le, expr.Parameters);
return filteredEntityCollection.Where(lambda);
but it basically gives me the same result.
Func<TEntity, string> is a C# function so that cannot possibly be executed in the SQL server. However, if you change that to an expression, it could work:
private readonly Expression<Func<TEntity, string>> fieldWhichMustEqualValue;
However, now the Filter function would not compile since you can't invoke expressions. What you need to do is to compose a new expression:
public static UnaryExpression WrapInPropertyAccess(this ConstantExpression constant) {
Tuple<object> container = new Tuple<object>(constant.Value);
Expression containerExpression = Expression.Constant(container, typeof(Tuple<object>));
MemberExpression propAccess = Expression.Property(containerExpression, "Item1");
UnaryExpression result = Expression.Convert(propAccess, constant.Type); // Cast back the object to the value type
return result;
}
private Expression<TEntity, bool> FieldEqualsValue(string value) {
var comparison = Expression.Equal(fieldWhichMustEqualValue.Body, Expression.Constant(value).WrapInPropertyAccess());
return Expression.Lambda<Func<TEntity, bool>>(comparison, fieldWhichMustEqualValue.Parameters);
}
public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
var condition = FieldEqualsValue(value);
return filteredEntityCollection.Where(condition);
}
The WrapInPropertyAccess function is not strictly needed, but using it makes Entity Framework generate a parameterized expression.

Extension method with optional generic argument

I need to implement an API based on extensions methods (i.e. I have to use a static non-generic class). API should work smoothly with LINQ fluent API and mostly with IQueryable arguments. Like this:
public static class SomeExtensions
{
public static IQueryable<TEntity> SomeMethod<TEntity>(this IQueryable<TEntity> set, ... some arguments)
{
}
}
Now, suppose the method should take some arguments plus an Expression<Func<TEntity, TResult>> one:
public static IQueryable<TEntity> SomeMethod<TEntity, TResult>(
this IQueryable<TEntity> set,
...,
Expression<Func<TEntity, TResult>> orderByExpression)
{
}
I would like to pass the orderByExpression to OrderBy method of fluent API. Or do somethong else if orderByExpression == null.
Naturally, I'd like to have something like this:
public static IQueryable<TEntity> SomeMethod<TEntity, TResult>(
this IQueryable<TEntity> set,
...,
Expression<Func<TEntity, TResult>> orderByExpression = null)
{
}
...but when calling this method w/o optional argument I have to implicitly pass generic types, because compiler doesn't know the type of TResult.
I see some possible approaches, but I don't like them much.
Define two methods: one with this argument and one w/o, and call the first from the second. I don't like it because, actually, there are many such methods in the API, and I would have to define one additional method for every one of them.
Use Expression<Func<TEntity, object>> instead of Expression<Func<TEntity, TResult>> (it is currently so). I got rid of generic type, but there is a problem with simple (value) types like int: LINQ raises an exception when trying to cast System.Int32 to System.Object.
Maybe (haven't tried yet) I could use Expression<Func<TEntity, dynamic>> - but I don't think this is a good approach at all.
Any other ideas, anyone?
Option (1) is the best from the caller perspective. Remember the main goal for the API is to make the caller's life easier, so putting additional efforts on the implementation side should be worth enough.
Option (3) is not good. You don't want to enter complications introduced by dynamic types. And EF does not like dynamic expressions.
Option (2) actually is not so bad. So if it's what you use currently, you can stay on it. All you need to make EF happy is to convert the passed expression by removing the Convert introduced for value type properties. To do so, you can use the following helper method:
internal static IQueryable<T> ApplyOrderBy<T>(
this IQueryable<T> source,
Expression<Func<T, object>> orderByExpression = null)
{
if (orderByExpression == null) return source;
var body = orderByExpression.Body;
// Strip the Convert if any
if (body.NodeType == ExpressionType.Convert)
body = ((UnaryExpression)body).Operand;
// Create new selector
var keySelector = Expression.Lambda(body, orderByExpression.Parameters[0]);
// Here we cannot use the typed Queryable.OrderBy method because
// we don't know the TKey, so we compose a method call instead
var queryExpression = Expression.Call(
typeof(Queryable), "OrderBy", new[] { typeof(T), body.Type },
source.Expression, Expression.Quote(keySelector));
return source.Provider.CreateQuery<T>(queryExpression);
}
Here is a small test showing how the above works for different property types:
var input = new[]
{
new { Id = 2, Name = "B", ParentId = (int?)1 },
new { Id = 1, Name = "A", ParentId = (int?)null },
}.AsQueryable();
var output1 = input.ApplyOrderBy(e => e.Id).ToList();
var output2 = input.ApplyOrderBy(e => e.Name).ToList();
var output3 = input.ApplyOrderBy(e => e.ParentId).ToList();
Sample usage with your example:
public static IQueryable<TEntity> SomeMethod<TEntity>(
this IQueryable<TEntity> source,
...,
Expression<Func<TEntity, object>> orderByExpression = null)
{
var result = source;
result = preprocess(result);
result = result.ApplyOrderBy(orderByExpression);
result = postprocess(result);
return result;
}
The first option you specify is the obvious and the cleanest, though most maintenance-heavy way to do this.
Additionally, you could introduce another step in your fluent syntax. Like defining:
public interface ISortableQueryable<T> : IQueryable<T>
{
IQueryable<T> WithSorting<TResult>(Expression<Func<TEntity, TResult>> orderByExpression);
}
returning it:
public static ISortableQueryable<TEntity> SomeMethod<TEntity>(
this IQueryable<TEntity> #this, ...)
{ ... }
and providing implementation of this interface where regular IQueryable calls either redirect to the IQueryable instance it receives in constructor, or some logic is performed based on the fact whether the WithSorting method was called or not.

Returning filtered IQueryable<T> with anonymous types

I have a set of Reports which I need to perform filtering on before returning the output. I would like to perform this with a single anonymous method to avoid duplicating the same code in different repositories. I'm using Entity Framework so the model types all related to the database and inherit from a base class called ReportBase.
This is what how I currently implement the filtering, each report type has to implement this method with a different context and returning a different IQueryable type.
private IQueryable<ReviewAgreement> GetFiltered(ReportFilter filter)
{
IQueryable<ReviewAgreement> reviewAgreementQueryable = Context.ReviewAgreements.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reviewAgreementQueryable = reviewAgreementQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reviewAgreementQueryable;
}
I've been trying to implement this anonymously so I can reuse it, as in this non functional example.
public IQueryable<T> GetFiltered(ReportFilter filter)
{
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable.Where(p => p.ClientWorkflowId == filter.ClientWorkflowId);
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable.Where(p => p.AppraisalLevelId == filter.AppraisalLevelId.Value);
}
return reportQueryable;
}
The issue I am having is of course that the use of Where is ambiguous, so it cannot resolve p.ClientWorkflowId.
I have tried using a Func<T, TResult> delegate to pass in the filtering options this but the Where operation seems to want to return a list.
Is there actually a method I can use to achieve the effect I want?
Declare an interface that has the two ID properties that you need to perform this operation.
Ensure that your entities implement that interface.
Add a constraint to the generic argument that it implements that interface.
Note that if your base class defines both of the properties in question then you don't need an interface, and can simply constrain the type to that base class:
public IQueryable<T> GetFiltered<T>(ReportFilter filter) where T : ReportBase
{
// body unchanged
}
If you want to go down the route of accepting parameters to represent these properties than it's also possible. The first thing is that you'll need to accept expressions, not Func objects, so that the query provider can analyze them. This means changing the function signature to:
public IQueryable<T> GetFiltered<T>(ReportFilter filter,
Expression<Func<T, int>> clientIdSelector,
Expression<Func<T, int>> appraisalIdSelector)
{
Next, to turn these selectors into predicates that compare the value to an ID that we have is a bit more involved for expressions than it is for regular delegates. What we really need here is a Compose method; for delegates it's simple enough, to compose one method with another you just invoke it with the parameter being the result of the first. With expressions this means taking the body of one expression and replacing all instance of the parameter with the body of another, and then wrapping the whole thing up in a new Lambda.
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
This itself is dependent on the ability to replace all instances of one expression with another. To do that we'll need to use the following:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Now that we have all of this in place we can actually compose our selectors with a comparison to the ID values of the filter:
IQueryable<T> reportQueryable = Context.Set<T>();
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.ClientWorkflowId));
if (filter.AppraisalLevelId.HasValue)
{
reportQueryable = reportQueryable
.Where(clientIdSelector.Compose(id => id == filter.AppraisalLevelId.Value));
}
return reportQueryable;

AutoMapper for Func's between selector types

I have two types: Cat and Dog. I'd like to select Cats using a Func<Dog, bool>. To do that, I need a way to map the properties from Cat to Dog in some sort of mapper (similar to how AutoMapper maps properties from one object to another type of object).
I'm imagining something like this:
public Cat GetCat(Func<Dog, bool> selector)
{
Func<Cat, bool> mappedSelector = getMappedSelector(selector);
return _catRepository.Get(mappedSelector);
}
private Func<Cat, bool> getMappedSelector(Func<Dog, bool> selector)
{
//some code here to map from one function type to another
//something like AutoMapper would be sweet...
//something that I can configure how I want the properties to be mapped.
}
Either there's already something that does this or there should be.
Here's a solution using AutoMapper:
Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
UPDATE: It's been 1.5 years since I first answered this, and I figured I'd expand on my answer now since people are asking how to do this when you have an expression as opposed to a delegate.
The solution is the same in principle - we need to be able to compose the two functions (selector and mapper) into a single function. Unfortunately, since there's no way in C# to "call" one expression from another (like we could with delegates), we can't directly represent this in code. For example, the following code will fail to compile:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
return mappedSelector;
}
The only way to create our composed function, therefore, is to build up the expression tree ourselves using the System.Linq.Expressions classes.
What we really need to do is to modify the body of the selector function so that all instances of its parameter are replaced by the body of the mapper function. This will become the body of our new function, which will accept mapper's parameter.
To replace the parameter I created a subclass of ExpressionVisitor class that can traverse an expression tree and replace a single parameter with an arbitrary expression:
class ParameterReplacer : ExpressionVisitor
{
private ParameterExpression _parameter;
private Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}
Then I created an extension method, Compose(), that uses the visitor to compose two lambda expressions, an outer and an inner:
public static class FunctionCompositionExtensions
{
public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
{
return Expression.Lambda<Func<X ,Y>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}
Now, with all that infrastructure in place, we can modify our GetMappedSelector() method to use our Compose() extension:
Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
return mappedSelector;
}
I created a simple console application to test this out. Hopefully, my explanation was not too obfuscated; but unfortunately, there isn't really a simpler approach to doing what you're trying to do. If you are still totally confused, at least you can reuse my code and have gained an appreciation for the nuances and complexities of dealing with expression trees!
#luksan, thanks for the inspiration! Your solution didn't solve my problem, but got me thinking. Since I needed to pass the translated expression to IQueryable.OrderBy(), using the inside-expression translation approach didn't work. But I came up with a solution that will work on both cases and is also simpler to implement. It's also generic so can be reused for any mapped types. Here is the code:
private Expression<Func<TDestination, TProperty>> GetMappedSelector<TSource, TDestination, TProperty>(Expression<Func<TSource, TProperty>> selector)
{
var map = Mapper.FindTypeMapFor<TSource, TDestination>();
var mInfo = ReflectionHelper.GetMemberInfo(selector);
if (mInfo == null)
{
throw new Exception(string.Format(
"Can't get PropertyMap. \"{0}\" is not a member expression", selector));
}
PropertyMap propmap = map
.GetPropertyMaps()
.SingleOrDefault(m =>
m.SourceMember != null &&
m.SourceMember.MetadataToken == mInfo.MetadataToken);
if (propmap == null)
{
throw new Exception(
string.Format(
"Can't map selector. Could not find a PropertyMap for {0}", selector.GetPropertyName()));
}
var param = Expression.Parameter(typeof(TDestination));
var body = Expression.MakeMemberAccess(param, propmap.DestinationProperty.MemberInfo);
var lambda = Expression.Lambda<Func<TDestination, TProperty>>(body, param);
return lambda;
}
Here is the ReflectionHelper code (used just to keep the code above cleaner)
private static class ReflectionHelper
{
public static MemberInfo GetMemberInfo(Expression memberExpression)
{
var memberExpr = memberExpression as MemberExpression;
if (memberExpr == null && memberExpression is LambdaExpression)
{
memberExpr = (memberExpression as LambdaExpression).Body as MemberExpression;
}
return memberExpr != null ? memberExpr.Member : null;
}
}

Expressions: a get-by-ID-selector when I have the Expression to get the ID?

I have a generic base repository class for LINQ-to-Entities that can do things like selecting an entity based on the ID.
In order to subclass it, you need to implement these two:
protected override Expression<Func<User, bool>> GetByIDSelector(int id)
{
return u => u.UserID == id;
}
protected override Expression<Func<User, int>> KeySelector
{
get { return u => u.UserID; }
}
I would like to just have the KeySelector (which is used for default sort order).
My 'get by ID' method looks like this:
public virtual TDataModel GetByID(TKey id)
{
var entity = SetProvider.Set<TDataModel>().Where(GetByIdSelector(id)).SingleOrDefault();
if (entity == null)
{
throw new EntityNotFoundException<TDataModel>(id);
}
return entity;
}
Where GetByIdSelector looks like this, which should turn the KeySelector expression into one that can select by ID.
private Expression<Func<TDataModel, bool>> GetByIdSelector(TKey id)
{
return Expression.Lambda<Func<TDataModel, bool>>
(
Expression.Equal(KeySelector.Body,
Expression.Constant(id)),
KeySelector.Parameters.Single()
);
}
However, the Entity Framework throws an exception that the parameter passed in by that isn't bound to the query.
The parameter 'u' was not bound in the specified LINQ to Entities query expression.
Is there any way I can cleanly reuse the KeySelector Expression without having to rebuild the entire thing?
The reason this doesn't work is because every time you call this.KeySelector, it's creating a brand new lambda expression (hence the call to KeySelector.Parameters.Single() is not the same parameter as the parameter used for KeySelector.Body). Cache that expression into a variable as follows and it will work.
private Expression<Func<TDataModel, bool>> GetByIdSelector(TKey id)
{
var keySelector = KeySelector;
return Expression.Lambda<Func<TDataModel, bool>>(
Expression.Equal(
keySelector.Body,
Expression.Constant(id)
),
keySelector.Parameters.Single()
);
}
What I would probably do is rather fix the code in the KeySelector property to use a fixed value such that the value retrieved is always the same instance.
Example:
private static readonly Expression<Func<User, int>> _keySelector = u => u.UserID;
protected override Expression<Func<User, int>> KeySelector
{
get { return _keySelector; }
}
Then if you do that - your old code for GetByIdSelector is actually no worse than my version above.

Categories