Wie konvertiert man aus Ausdruck > Prädikat für Ausdruck Prädikat

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

Frage

Etwas Hintergrund: Ich habe in meiner Anwendung mehrere Schichten, von denen zwei eine Domänenschicht und eine Infrastrukturschicht sind, die als meine DAL dient. In der Domain-Ebene habe ich ein generisches Repository-Muster als solches implementiert:

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

In meiner DAL habe ich ein generisches DAO-Muster implementiert:

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

Ich habe eine Domain-Klasse, die in geschäftlicher Hinsicht eine Person darstellt. Ich habe ein ähnliches individuelles Objekt in meiner DAL-Schicht, das verwendet wird, um das Objekt in meiner Datenbank darzustellen. Ich habe eine Klasse namens EntityFrameworkDao, die meine IDAO-Schnittstelle implementiert und für alle Aktionen verantwortlich ist, die in dieser DAO-Schnittstelle gefunden werden (CRUD und einige andere Aktionen wie oben beschrieben).

Ich versuche (Tage des Versuchs), einen Weg zu finden, den Ausdruck, der im Repository verwendet wird, dem Ausdruck zuzuordnen, der in der DAL verwendet wird. Das konkrete Beispiel ist dies: Ich habe ein generisches DomainRepository, das meine IRepository-Schnittstelle implementiert (siehe oben). Die SearchFor-Methode sieht folgendermaßen aus:

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

Ich muss herausfinden, was SomeMagicFunctionToConvertExpressions ist, damit ich das Prädikat in meiner Domänenebene in etwas konvertieren kann, das die Where-Methode in meiner DAL-Klasse, die IDao implementiert, verstehen kann:

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

Ich habe versucht, Automapper CreateMapExpression wie in diesem Artikel gefunden: AutoMapper für Func zwischen Selektor-Typen

Aber das sagt mir nur, wie man zwischen Func<DomainType,bool> Prädikat in Func<DTOType,bool> (Prädikat), nicht Expression in Expression Func<DTOType,bool> . Ich bin auf der Suche nach einem Weg, um etwas wie das zu konvertieren:

Expression<Func<TDomainEntity, bool>> predicate

Zu diesem:

Expression<Func<TDAOEntity, bool>> predicate

Ich dachte, dass ich etwas machen würde, nachdem ich diesen Artikel über das Mutieren von Expression-Bäumen gefunden hatte, aber ich werde es nicht zulassen, dass ich einen Complex-Linq-Ausdruck übergebe, der && oder || enthält oder es ist komplexer als eine einfache Anfrage vom Typ i => i.id.Equals (12345).

Ich benutze Automapper, also wären alle Solutoins großartig, aber ich bin offen für alle Ideen an diesem Punkt. Ich stecke wirklich fest und recherchiere seit Tagen daran. Es scheint eine ziemlich häufige Aufgabe zu sein: Konvertiere eine Abfrage basierend auf einem Typ von einer Schicht in der Architektur zu einem Typ, der in der DAL verwendet wird.

Vielen Dank im Voraus für Ihre Hilfe.

Beliebte Antwort

Du solltest das wahrscheinlich nicht tun, es sieht so aus, als ob du die Dinge zu sehr verkomplizierst.

Erstens: Wenn Sie ein generisches Repository-Muster verwenden, verletzen Sie grundsätzlich das TDA- Prinzip; Sie erstellen eine direkte Abhängigkeit von Ihrer Speicherimplementierung. Es ist in Ordnung, eine "Alle", "Nach Id" und "Volltextsuche" in Ihrem generischen Repository zu haben, aber alle anderen sollten von Ihrer Schnittstelle ausgeblendet werden. Ein einfaches Beispiel:

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

Nehmen wir an, dies ist eine einfache Flagge, und irgendwann später ändert sich Ihre Domain irgendwo, und Sie haben entschieden, dass ein Element auch gelöscht werden kann. Dies würde bedeuten, dass Sie Ihren Code überall in der Domäne von der ersten in die folgende ändern müssen:

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

Der richtige Weg, um dies zu tun, wäre eine geeignete Methode für Ihr Repository wie folgt

repo.ActiveItems()

Dies stellt sicher, dass Sie Ihr Verhalten nicht überall verbreiten.

Wenn Sie eine generische Suche veröffentlichen, sollten Sie als Nächstes Linq direkt in Ihrem Domänenmodell verwenden. Schließlich implementieren Sie immer noch eine Ebene über einer anderen Ebene Ihrer SQL-Objekte. Können Sie mir den zusätzlichen Nutzen dieser zusätzlichen Schichten erklären? Immerhin sind sie immer noch an die gleiche Implementierung gebunden (auch wenn sie dort eine zusätzliche Namens- / Wertumwandlung enthalten).



Related

Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow