Come convertire da Expression > predicato su Expression predicato

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

Domanda

Qualche sfondo: ho nella mia applicazione più livelli, due dei quali sono un livello dominio e un livello infrastruttura che funge da mio DAL. Nel livello del dominio, ho implementato un modello di repository generico come tale:

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

Nel mio DAL ho un modello DAO generico implementato come tale:

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

Ho una classe di dominio che rappresenta, in termini commerciali, un individuo. Ho un oggetto singolo simile nel mio strato DAL che viene utilizzato per rappresentare l'oggetto nel mio database. Ho una classe chiamata EntityFrameworkDao che implementa la mia interfaccia IDAO ed è responsabile di tutte le azioni trovate nell'interfaccia DAO (CRUD e alcune altre azioni come descritto sopra).

Sto provando (giorni di tentativi) per trovare un modo per mappare l'espressione che è usata nel repository per l'espressione usata nel DAL. L'esempio specifico è questo: ho un DomainRepository generico che implementa la mia interfaccia IRepository (vedi sopra). Il metodo SearchFor ha il seguente aspetto:

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

Ho bisogno di capire cosa sia SomeMagicFunctionToConvertExpressions in modo da poter convertire il predicato nel mio livello di dominio in qualcosa che il metodo Where può capire nella mia classe DAL che implementa IDao:

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

Ho provato ad usare CreateMapExpression di Automapper come trovato in questo articolo: AutoMapper per Func tra tipi di selettori

Ma questo mi dice solo come convertire tra Func<DomainType,bool> predicate a Func<DTOType,bool> (predicato), non Expression to Expression. Sto cercando un modo per convertire qualcosa del genere:

Expression<Func<TDomainEntity, bool>> predicate

A questa:

Expression<Func<TDAOEntity, bool>> predicate

Ho pensato che stavo per qualcosa dopo aver trovato questo articolo sulla mutilazione degli alberi di espressione, ma non mi permetterà di passare in un'espressione di linux compex che contiene && o || o più complesso di una semplice query i => i.id.Equals (12345).

Sto usando Automapper, quindi qualsiasi soluto che usi sarebbe fantastico, ma a questo punto sono aperto a qualsiasi idea. Sono davvero bloccato e ho fatto ricerche per giorni. Sembra un compito abbastanza comune: convertire una query basata su un tipo da un livello nell'architettura a un tipo utilizzato nel DAL.

Grazie in anticipo per il vostro aiuto.

Risposta popolare

Probabilmente non dovresti farlo, sembra che tu stia complicando troppo le cose.

Primo: se usi un modello di repository generico stai violando sostanzialmente il principio TDA ; stai creando una dipendenza diretta all'implementazione dello storage. Va bene avere un "tutto", "da Id" e "ricerca full text" nel tuo repository generico, ma tutti gli altri dovrebbero essere nascosti dalla tua interfaccia. Un semplice esempio:

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

Supponiamo che questo sia un semplice flag, e in qualche modo più tardi il tuo dominio cambia da qualche parte, e hai deciso che un oggetto può essere anche cancellato. Ciò implica che è necessario modificare il codice in tutto il dominio dal primo al seguente:

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

Il modo corretto per farlo, sarebbe avere un metodo corretto sul tuo repository come questo

repo.ActiveItems()

Ciò ti assicura di non diffondere il tuo comportamento ovunque.

Quindi, se stai esponendo una ricerca generica, perché non utilizzare Linq direttamente nel tuo modello di dominio; dopo tutto, stai ancora implementando un livello su un altro livello degli oggetti sql. Potresti spiegarmi il valore aggiunto di aggiungere questi strati extra? Dopotutto, sono ancora legati alla stessa implementazione (anche se la loro potrebbe essere una conversione extra di nome / valore).



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché