Cómo convertir de expresión > predicado a la expresión predicado

automapper domain-driven-design entity-framework expression-trees linq

Pregunta

Algunos antecedentes: tengo en mi aplicación varias capas, dos de las cuales son una capa de dominio y una capa de infraestructura que sirve como mi DAL. En la capa de dominio, he implementado un patrón de repositorio genérico como tal:

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

En mi DAL tengo un patrón DAO genérico implementado como tal:

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

Tengo una clase de dominio que representa, en términos comerciales, un individuo. Tengo un objeto individual similar en mi capa DAL que se utiliza para representar el objeto en mi base de datos. Tengo una clase llamada EntityFrameworkDao que implementa mi interfaz IDAO y es responsable de todas las acciones encontradas en esa interfaz DAO (CRUD y algunas otras acciones como se describe anteriormente).

Estoy tratando (días de intentarlo) de encontrar una manera de asignar la expresión que se usa en el repositorio a la expresión que se usa en el DAL. El ejemplo específico es este: tengo un DomainRepository genérico que implementa mi interfaz IRepository (ver arriba). El método SearchFor se ve así:

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

Necesito averiguar qué es SomeMagicFunctionToConvertExpressions para poder convertir el predicado en mi capa de dominio en algo que el método Where pueda entender en mi clase DAL que implementa IDao:

        public IQueryable<TEfEntity> Where(Expression<Func<TEfEntity, bool>> predicate)
        {
            return _context.Set<TEfEntity>().Where(predicate).AsQueryable();
        }

He intentado usar CreateMapExpression de Automapper como se encuentra en este artículo: AutoMapper for Func's entre los tipos de selector

Pero eso sólo me dice cómo convertir entre Func<DomainType,bool> predicado a Func<DTOType,bool> (predicado), no Expresión de Expresión. Estoy buscando una manera de convertir algo como esto:

Expression<Func<TDomainEntity, bool>> predicate

A esto:

Expression<Func<TDAOEntity, bool>> predicate

Pensé que estaba en algo después de encontrar este artículo sobre mutiar árboles de expresiones, pero no me permitirá pasar una expresión compex linq que contenga && o || o cualquier cosa más compleja que una simple consulta de tipo i => i.id.Equals (12345).

Estoy usando Automapper, por lo que cualquier uso de solutoins sería genial, pero estoy abierto a cualquier idea en este momento. Realmente estoy atascado y he estado investigando esto por días ahora. Parece una tarea bastante común: convertir una consulta basada en un tipo de una capa en la arquitectura a un tipo usado en el DAL.

Gracias de antemano por tu ayuda.

Respuesta popular

Probablemente no deberías estar haciendo esto, parece que estás complicando demasiado las cosas.

Primero: si usa un patrón de repositorio genérico, básicamente está violando el principio TDA ; está creando una dependencia directa a su implementación de almacenamiento. Está bien tener un "todo", "por Id" y "búsqueda de texto completo" en su repositorio genérico, pero cualquier otro debe estar oculto por su interfaz. Un ejemplo simple:

repo.search(x=>x.IsActive==true)

Supongamos que se trata de una simple bandera y, de alguna manera, más tarde, su dominio cambia en algún lugar, y decidió que un elemento también puede borrarse por software. Esto implicaría que necesita cambiar su código en cualquier parte del dominio del anterior al siguiente:

repo.search(x=>x.IsActive==true && x.IsDeleted == false)

La forma correcta de hacer esto, sería tener un método adecuado en su repositorio como este

repo.ActiveItems()

Esto asegura que no se propague su comportamiento en todas partes.

A continuación, si está exponiendo una búsqueda genérica, ¿por qué no usa Linq directamente en su modelo de dominio? después de todo, todavía está implementando una capa sobre otra capa de sus objetos SQL. ¿Podría explicarme el valor añadido de agregar estas capas adicionales? Después de todo, todavía están vinculados a la misma implementación (a pesar de que podría haber una conversión de nombre / valor adicional allí).



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow