如何将Expression<Func<DomainType>>谓词转换为Expression<Func<DTOtype>>谓词?

3

一些背景信息:我的应用程序中有多个层,其中两个是领域层和基础设施层,后者作为我的数据访问层。在领域层中,我实现了一个通用的存储库模式,如下所示:

    public interface IRepository<T, in TId> where T : IEntity<TId>
    {
        void Insert(T entity);
        void Delete(T entity);
        IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate);
        IQueryable<T> GetAll();
        T GetById(TId id);
    }

在我的数据访问层(DAL)中,我实现了一个通用的DAO模式,如下所示:
    public interface IDao<TEntity> where TEntity : class
    {
        IQueryable<TEntity> Select();
        IQueryable<TEntity> GetAll();
        IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> predicate);
        TEntity GetSingle(Expression<Func<TEntity, bool>> predicate);
        TEntity GetFirst(Expression<Func<TEntity, bool>> predicate);
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Attach(TEntity entity);
    }

我有一个领域类,代表业务术语中的个人。我在我的DAL层中有一个类似的个人对象,用于表示我的数据库中的对象。我有一个名为EntityFrameworkDao的类,实现了我的IDAO接口,并负责该DAO接口中找到的所有操作(CRUD和其他一些操作,如上所述)。
我正在尝试(尝试了几天)找到一种将存储库中使用的表达式映射到DAL中使用的表达式的方法。具体示例是这样的:我有一个通用的DomainRepository,实现了我的IRepository接口(见上文)。SearchFor方法看起来像这样:
        public IQueryable<TDomainEntity> SearchFor(Expression<Func<TDomainEntity, bool>> predicate)
    {
        var convertedExpression = **SomeMagicFunctionToConvertExpressions**();
        var dataEntities = _dao.Where(convertedExpression);
        return AutoMapper.Mapper.Map<IEnumerable<TEfEntity>, IEnumerable<TDomainEntity>>(dataEntities).AsQueryable();
    }

我需要找出一些魔术函数来转换表达式,这样我就能够在实现IDao的DAL类中使用Where方法,并将域层中的谓词转换为可理解的内容。
        public IQueryable<TEfEntity> Where(Expression<Func<TEfEntity, bool>> predicate)
        {
            return _context.Set<TEfEntity>().Where(predicate).AsQueryable();
        }

我尝试使用Automapper的CreateMapExpression,就像在这篇文章中所提到的那样:AutoMapper for Func's between selector types。但是这只告诉我如何在Func<DomainType,bool>谓词和Func<DTOType,bool>(谓词)之间进行转换,而不是表达式与表达式之间的转换。我正在寻找一种将类似于这样的内容转换的方法:
Expression<Func<TDomainEntity, bool>> predicate

到这个:
Expression<Func<TDAOEntity, bool>> predicate

我看到了这篇文章关于表达式树的变异,觉得很有帮助,但它不允许我传递包含&&或||或任何比简单的i => i.id.Equals(12345)查询更复杂的linq表达式。

我正在使用Automapper,所以任何使用它的解决方案都很好,但我现在对任何想法都持开放态度。我真的卡住了,已经研究了几天了。它似乎是一个相当常见的任务:将基于一种架构中的类型的查询转换为在DAL中使用的类型的查询。

提前感谢您的帮助。


4
我认为你的方法有误:查询应该被隔离到一个层级中,你不应该需要在层级之间进行翻译。 - svick
另外,我认为你找到的Jon的解决方案是正确的方向,特别是由sinelaw改进的版本(https://dev59.com/V3E85IYBdhLWcg3wZymt#11997136)。你能解释一下当你尝试将其与更复杂的表达式一起使用时它具体失败了吗? - svick
请查看我在这里的相关答案:https://dev59.com/HlvUa4cB1Zd3GeqPr0np#7425211 - luksan
1个回答

2
你可能不应该这样做,看起来你正在过度复杂化事情。
首先:如果你使用通用仓储模式,你基本上违反了TDA原则;你正在创建对存储实现的直接依赖。在通用仓储中使用“全部”、“按ID”和“全文搜索”是可以的,但任何其他操作都应该被接口隐藏。一个简单的例子:
repo.search(x=>x.IsActive==true)

假设这是一个简单的标志,然后在某个时候你的域名发生了变化,你决定一个项目也可以被软删除。这意味着你需要在域中的每个地方将代码从原来的更改为以下内容:
repo.search(x=>x.IsActive==true && x.IsDeleted == false)

这样做的正确方式是在您的存储库上拥有一个适当的方法,像这样:
repo.ActiveItems()

这可以确保您不会将自己的行为随处散布。
接下来,如果您正在暴露一个通用搜索,为什么不直接在领域模型中使用Linq呢?毕竟,您仍然是在另一层SQL对象的顶部实现了一层。您能否解释一下添加这些额外层的增加值?毕竟,它们仍然与同一实现相关联(即使其中可能存在一些额外的名称/值转换)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接