Ein bisschen spezielle Behälterzauber

    In einem früheren Artikel habe ich ein Beispiel für eine Factory zum Empfangen von IQuery-Implementierungen angegeben, aber den Funktionsmechanismus nicht erläutert.
    _queryFactory.GetQuery()
        .Where(Product.ActiveRule)
        .OrderBy(x => x.Id)
        .Paged(0, 10) // получаем 10 продуктов для первой страницы
    // Мы решили подключить полнотекстовый поиск и добавили ElasticSearch, не вопрос:
    _queryFactory.GetQuery()
        .Where(new FullTextSpecification(«зонтик»))
        .All()
    // Или EF тормозит и мы решили переделать на хранимую процедуру и Dapper
    _queryFactory.GetQuery()
        .Where(new DictionarySpecification (someDirctionary))
        .All()
    

    In diesem Artikel möchte ich die Technik der Registrierung der erforderlichen Komponenten der Baugruppe nach Vereinbarung erläutern. Jetzt habe ich eine Codebasis mit einer anderen CQRS-Implementierung zur Hand, sodass sich die Beispiele unterscheiden. Dies ist nicht grundlegend: Die Grundidee bleibt unverändert. Angenommen

    , Sie haben eine Schnittstelle, in der ListParams eine Spezifikation ist, die vom Frontend stammt
    public interface IListOperation
    {
         ListResult List(ListParams listParam);
    }
    

    Die Aufgabe,
    Anwendungsentwicklern das Schreiben von Controllern, Projektionen und Diensten zu ermöglichen.


    Lösung
    Erstellen Sie eine Basisklasse für die Operation List:
        public class ListOperationBase : IListOperation
            where TEntity: IEntity
            where TDto: IHaveId
        {
            protected readonly IDbContext DbContext ;
            public ListOperationBase(IDbContext dbContext )
            {
                if (dbContext == null) throw new ArgumentNullException(nameof(dbContext));
                DbContext = dataStore;
            }
            public virtual ListResult List(ListParam listParam)
            {
                var data = AddProjectionBusinessLogic(AddEntityBusinessLogic(DataStore
                    .GetAll())
                    .ProjectTo())
                    .Filter(listParam);
                return new ListResult()
                {
                    Data = data
                        .Paging(listParam)
                        .ToList(),
                    TotalCount = data.Count()
                };
            }
            protected virtual IQueryable AddEntityBusinessLogic(IQueryable queryable) => queryable;
            protected virtual IQueryable AddProjectionBusinessLogic(IQueryable queryable) => queryable;
        }
    

    Die ProjectTo- Methode ist eine AutoMapper-Funktion , mit der Sie Projektionen gemäß Vereinbarungen erstellen können. Beseitigt die Notwendigkeit, die gesamte Entität in den Speicher zu heben , während das Schreiben langweiliger Select- Konstrukte des Formulars vermieden wird
    Query.Select(x => {
        Name = x.Name,
        ParentUrl = x.Parent.Url,
        Foo = x.Foo
    })
    

    Mit den virtuellen Methoden AddEntityBusinessLogic und AddProjectionBusinessLogic können Sie Filterbedingungen vor und nach dem Erstellen der Projektion hinzufügen.

    Jetzt können wir für das schnelle Prototyping ListOperationBase verwendenFür echte Implementierungen müssen Sie echte Operationen mit der richtigen Logik erstellen. Dazu müssen Sie zu Beginn der Anwendung alles, was sich in der Baugruppe befindet, nach Vereinbarung registrieren. In meinem Fall wird die modulare Architektur verwendet und dies ist der Modulladecode. Für monolithische Anwendungen müssen Sie auch eine Liste der Assemblys erstellen, aus denen Sie Typen laden möchten.
    var types = GetType().Assembly.GetTypes();
    var operations = types
    	.Where(t.IsClass
    		&& !t.IsAbstract
    		&& t.ImplementsOpenGenericInterface(typeof(IListOperation<>)));
    foreach (var operation in operations)
    {
    	var definitions =
    		operation.GetInterfaces().Where(i => i.ImplementsOpenGenericInterface(typeof (IListOperation<>)));
    	foreach (var definition in definitions)
    	{
    		Container.Register(definition, operation);
    	}
    	// ...
    }
    

    Sie benötigen nur einen Controller für alle Crud-Vorgänge. Die Implementierung des ControllerSelector für generische WebApi-Controller finden Sie unter: github.com/hightechtoday/costeffectivecode/blob/master/src/CostEffectiveCode.WebApi2/WebApi/Infrastructure/RuntimeScaffoldingHttpControllerSelector

    public ListResult List(ListParam loadParams) =>
      (_container.ResolveAll>().SingleOrDefault() ?? new ListOperationBase(DataStore))
      .List(loadParams);
    

    Das Übergeben des Containers an den Controller ist natürlich eine so oder so gute Idee ( ServiceLocator ), und tatsächlich ist es viel besser, den Aufruf in der Factory-Methode zu verpacken (wie im QueryFactory- Beispiel ). Eine weitere Schwachstelle ist, was zu tun ist, wenn zwei IListOperation- Implementierungen mit denselben Typen registriert sind . Es gibt keine einheitliche Antwort auf diese Frage: Alles hängt von den Besonderheiten Ihrer Anwendung und den Systemanforderungen ab.

    Als Ergebnis haben wir ein System für Rapid Prototyping erhalten, das den Programmierer vor dem Schreiben von Steuerungen und dem Registrieren von Diensten im Container bewahrt. Sie müssen lediglich eine Entität (DTO) hinzufügen und die Zuordnung beschreiben. Wenn Sie AutoMapper verwenden , sollten Sie auf jeden Fall das Mapper.AssertConfigurationIsValid () -Konstrukt hinzufügen .Es wird Ihnen helfen, Fehler zu erkennen, wenn Sie Entity oder Dto ändern müssen. Übrigens ist es analog zur Registrierung von Vorgängen möglich, die Erstellung von Zuordnungen unter Vereinbarungen für Fälle zu automatisieren, in denen alle Zuordnungen offensichtlich sind. In der Praxis müssen Sie der Zuordnung jedoch häufig mehrere Zeilen hinzufügen. Ich bevorzuge dies manuell, da es sich nur um ein paar Zeilen handelt.

    Schritt für Schritt
    1. Hinzufügen SomeEntity: IEntity
    2. Fügen Sie SomeEntityListDto hinzu
    3. Wir registrieren die Zuordnung SomeEntity -> SomeEntityListDto
    4. Ruft automatisch die Methode / SomeEntity / List ab
    5. Hinzufügen von Geschäftslogik zu SomeEntityListOperation
    6. Die Methode / SomeEntity / List beginnt, die neue Implementierung mit der „richtigen“ Geschäftslogik zu verwenden

    Die Zuordnung kann weggelassen werden, wenn die Entität "wie sie ist" problemlos an die Präsentationsebene übergeben / serialisiert werden kann.

    Jetzt auch beliebt: