Comment convertir Expression > prédicat à l'expression prédicat

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

Question

Un peu d’arrière-plan: dans mon application, j'ai plusieurs couches, dont deux sont une couche de domaine et une couche d’infrastructure qui sert de DAL. Dans la couche de domaine, j'ai implémenté un modèle de référentiel générique en tant que tel:

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

Dans mon DAL, j'ai un modèle DAO générique implémenté en tant que tel:

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

J'ai une classe de domaine qui représente, en termes commerciaux, un individu. J'ai un objet individuel similaire dans ma couche DAL qui est utilisé pour représenter l'objet dans ma base de données. J'ai une classe appelée EntityFrameworkDao qui implémente mon interface IDAO et est responsable de toutes les actions trouvées dans cette interface DAO (CRUD et quelques autres actions décrites ci-dessus).

J'essaie (en quelques jours) de trouver un moyen de mapper l'expression utilisée dans le référentiel avec l'expression utilisée dans le DAL. L'exemple spécifique est le suivant: j'ai un DomainRepository générique qui implémente mon interface IRepository (voir ci-dessus). La méthode SearchFor ressemble à ceci:

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

Je dois comprendre ce que SomeMagicFunctionToConvertExpressions est afin de pouvoir convertir le prédicat de ma couche de domaine en quelque chose que la méthode Where peut comprendre dans ma classe DAL qui implémente IDao:

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

J'ai essayé d'utiliser CreateMapExpression de Automapper comme dans cet article: AutoMapper pour Func entre les types de sélecteur

Mais cela ne me dit comment convertir Func<DomainType,bool> prédicat Func<DTOType,bool> (prédicat), pas d' expression à l' expression. Je cherche un moyen de convertir quelque chose comme ceci:

Expression<Func<TDomainEntity, bool>> predicate

Pour ça:

Expression<Func<TDAOEntity, bool>> predicate

Après avoir trouvé cet article sur la mutation des arbres d’expression, j’imaginais que quelque chose se passait, mais il ne me permet pas de passer une expression linux compex contenant && ou || ou tout plus complexe qu'une simple requête de type i => i.id.Equals (12345).

J'utilise Automapper, donc toute solution en solutoïne serait géniale, mais je suis ouvert à toute idée à ce stade. Je suis vraiment coincé et je fais des recherches à ce sujet depuis des jours. Cela semble être une tâche assez courante: convertir une requête basée sur un type d’une couche de l’architecture en un type utilisé dans le DAL.

Merci d'avance pour votre aide.

Réponse populaire

Vous ne devriez probablement pas faire cela, on dirait que vous compliquez les choses à outrance.

Premièrement: si vous utilisez un modèle de référentiel générique, vous violez le principe de la TDA ; vous créez une dépendance directe à votre implémentation de stockage. Il est bon d’avoir un "tout", "par ID" et "recherche de texte intégral" sur votre référentiel générique, mais tout autre doit être masqué par votre interface. Un exemple simple:

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

Supposons qu'il s'agisse d'un simple drapeau et que, plus tard, votre domaine change quelque part et que vous ayez décidé qu'un élément peut également être supprimé de manière logicielle. Cela impliquerait que vous deviez changer votre code partout dans le domaine, de l'ancien à celui-ci:

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

La bonne façon de faire cela serait d’avoir une méthode appropriée sur votre référentiel comme ceci

repo.ActiveItems()

Cela garantit que vous ne répandez pas votre comportement partout.

Ensuite, si vous exposez une recherche générique, pourquoi ne pas utiliser Linq directement dans votre modèle de domaine; après tout, vous continuez de mettre en œuvre une couche sur une autre couche de vos objets SQL. Pourriez-vous m'expliquer la valeur ajoutée de l'ajout de ces couches supplémentaires? Après tout, ils sont toujours liés à la même implémentation (même s’il pourrait y avoir une conversion nom / valeur supplémentaire).



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi