允许调用方在不知道值的情况下提供LINQ谓词

本文关键字:情况下 谓词 LINQ 不知道 许调用 调用 | 更新日期: 2023-09-27 18:29:49

我们正在使用LINQ to EF,我正在尝试编写一个CachingRepository类,该类将简化在单个存储库实例的生命周期中将检索到的实体缓存在内存中的模式。诀窍是,我希望能够指定缓存实体的属性作为构造函数的参数:

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

(现有的IRepository实现非常简单)。因此,如果有人想创建一个按Employee.BadgeCode缓存的CachingRepository(其中BadgeCode是一个字符串),他们可以使用现有的Employee存储库和:

var cacheRepo = new CachingRepository(employeeRepo, (Employee) emp => emp.BadgeCode);

以下是存储库的内容,缓存已经丢失,我想从数据库中检索:

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

然而,在尝试运行上面的例子时,我得到了一个异常

参数"emp"未绑定到指定的LINQ to Entities查询表达式中。

我确信简单地将_selector.Body传递给Equal表达式是错误的,但我应该如何做呢?我似乎需要让parameter"进入"我的_selector表达式,但我不知道该怎么做。

我意识到,我可以从构造函数中的选择器中提取属性名称,并使用类似Expression.Property(parameter, propertyName)的东西,但除了简单的属性选择器之外,还可以传递更复杂的选择器表达式,这将是一件好事。(更不用说我想了解为什么这不起作用)。

允许调用方在不知道值的情况下提供LINQ谓词

发生错误的原因是_selector.Body正在尝试访问'emp'参数,而您正在创建的lambda中不存在该参数。要修复它,您应该能够为传递给Expression.Lambda的参数重用_selector.Parameters[0],而不是创建新的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);
}