Reputation: 22843
We're using LINQ to EF and I'm trying to write a CachingRepository
class that will simplify the pattern of caching retrieved entities in memory for the lifecycle of a single repository instance. The trick is that I want to be able to specify the property by which the entities are cached as a parameter to the constructor:
public class CachingRepository<TEntity, TKey> where TEntity : class
{
private readonly Dictionary<TKey, TEntity> _cache
= new Dictionary<TKey, TEntity>();
private readonly IRepository<TEntity> _repository;
private readonly Expression<Func<TEntity, TKey>> _selector;
public CachingRepository(IRepository<TEntity> repository, Expression<Func<TEntity, TKey>> entityKeySelector)
{
_repository = repository;
_selector = entityKeySelector;
}
}
(The existing IRepository
implementation is pretty straightforward). So if someone wants to create a CachingRepository that caches per Employee.BadgeCode
for example (where BadgeCode
is a string), they could take an existing Employee repository and:
var cacheRepo = new CachingRepository(employeeRepo, (Employee) emp => emp.BadgeCode);
Here's the meat of the repository, where the cache has missed and I want to retrieve from the database:
private TEntity GetFromStore(TKey key)
{
var entityType = typeof(TEntity);
var paramName = _selector.Parameters[0].Name;
var parameter = Expression.Parameter(entityType, paramName);
var lambda = Expression.Lambda<Func<TEntity, bool>>(
Expression.Equal(
_selector.Body,
Expression.Constant(key)
)
, parameter);
return _repository.FirstOrDefault(lambda);
}
However, in trying to run the above example I get an exception
The parameter 'emp' was not bound in the specified LINQ to Entities query expression.
I'm sure that I'm doing something wrong in simply passing _selector.Body
to the Equal
expression, but how should I do it? It seems like I need to have parameter
"get into" my _selector
expression but I'm not sure how to do that.
I realize that I could just pluck the property name from the selector in the constructor, and use something like Expression.Property(parameter, propertyName)
instead, but it'd be nice to be able to pass more complex selector expressions besides simple property selectors. (Not to mention I'd like to understand why this isn't working).
Upvotes: 1
Views: 101
Reputation: 7135
The errors occurs because _selector.Body
is trying to access the 'emp' parameter, and that doesn't exist in the lambda
you're creating. To fix it you should just be able to reuse _selector.Parameters[0]
for the parameter you pass to Expression.Lambda
instead of creating the new parameter
ParameterExpression
:
private TEntity GetFromStore(TKey key)
{
var lambda = Expression.Lambda<Func<TEntity, bool>>(
Expression.Equal(
_selector.Body,
Expression.Constant(key)
)
, _selector.Parameters[0]);
return _repository.FirstOrDefault(lambda);
}
Upvotes: 1