2017-11-13 1 views
1

Ich habe eine Anwendung, wo ich mehrere Verbindungszeichenfolgen für meine DbContext Implementierungen haben. Um dies in den verbrauchenden Klassen zu unterstützen, habe ich eine IDbContextProvider Schnittstelle mit einer Get Methode erstellt, die mir die DbContext Instanzen zur Verfügung stellen kann, die ich brauche.Einfache Injektor Dekorator und Kovarianz

Ich habe auch eine ICommandHandler Sache gehen, und ich versuche, einen Dekorateur zu erstellen, der DbContext.SaveChangesAsync() bei erfolgreicher Befehlsausführung aufrufen wird. Ich Registrierung meine Dekorateur wie folgt aus:

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(SaveChangesCommandHandlerDecorator<>)); 

Nun, da will ich nicht einen Typparameter für die DbContext implementaiton hinzuzufügen, und ich kenne alle von DbContext abgeleiteten Klassen haben eine SaveChangesAsync() Methode, dachte ich, ich könnte Kovarianz verwenden. Also meine Schnittstelle sieht wie folgt aus:

public public interface IDbContextProvider<out TDbContext> where TDbContext : DbContext 
{ 
    TDbContext Get(DbPrivileges privileges); 
} 

und der relevante Teil meiner Dekorateur:

public class SaveChangesCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> 
    where TCommand : ICommand 
{ 
    private readonly ICommandHandler<TCommand> _handler; 
    private readonly IDbContextProvider<DbContext> _dbContextProvider; 

    public SaveChangesCommandHandlerDecorator(
     ICommandHandler<TCommand> handler, IDbContextProvider<DbContext> dbContextProvider) 
    { 
     _handler = handler; 
     _dbContextProvider = dbContextProvider; 
    } 

    ... 

Allerdings, wenn ich Verify() auf meinem Behälter nennen, wirft sie der IDbContextProvider ungültig ist, wie es sieht für die "Basis" IDbContextProvider<DbContext> statt einer der in meiner App registrierten.

Der Konstruktor vom Typ SaveChangesCommandHandlerDecorator < CreateUserCommand> enthält den Parameter mit dem Namen 'dbContextProvider' und geben IDbContextProvider < DbContext>, die IDbContextProvider < DbContext nicht registered.Please sicherzustellen> registriert ist, oder den Konstruktor von SaveChangesCommandHandlerDecorator < CreateUserCommand ändern >. Beachten Sie, dass eine Registrierung für einen anderen Typ (Redacted) vorhanden ist. IDbContextProvider < TDbContext> während der angeforderte Typ ist (Redacted) .IDbContextProvider < Microsoft.EntityFrameworkCore.DbContext>.

Das macht tatsächlich Sinn, da einfache Injector zu wissen, keine Möglichkeit hat, welche konkreten Typ in den dbContextProvider Parameter zu injizieren.

Gibt es eine Möglichkeit, die Art und Weise der Anpassung meines Dekorateur erstellt wird, so dass es auf den Abhängigkeiten der zugrunde liegenden ICommandHandler Implementierung der Abhängigkeiten spähen kann, und wählen Sie die IDbContextProvider Signatur von dort über die Schöpfung? Also, wenn mein Command-Handler eine IDbContextProvider<AwesomeDbContext> hat, möchte ich, dass das auch für meinen Decorator gelöst wird.

Antwort

2

Also lassen Sie mich das klarstellen: Ihre Anwendung enthält mehrere DbContext implemtantations, wie zum Beispiel:

  • AwesomeDbContext
  • EventBetterDbContext
  • BrilliantDbContext

Nun hat jeder Handler Befehl Regel wird davon abhängen, eine bestimmte DbContext Implementierung, indem Sie eine IDbContextProvider<TDbContext> wo die TDbContext ist die spezifische DbContext Implementierung.

Jetzt, je nachdem, was der Decorator-Befehls-Handler abhängt, möchten Sie genau diese IDbContextProvider<TDbContext> Implementierung in den Decorator auch, so dass Sie die Änderungen für diese bestimmte Instanz speichern können.

Wenn ich Ihre Frage richtig zusammenfasse, lautet die kurze Antwort auf Ihre Frage: Nein, das können Sie nicht tun.

Die längere Antwort ist, ja das ist tatsächlich möglich, durch ein zusätzliches generisches Typargument für das Hinzufügen TDbContext zu Ihrem SaveChangesCommandHandlerDecorator<TCommand, TDbContext> Dekorateur, und verwenden Sie die RegisterDecorator Überlastung, die die Func<DecoratorPredicateContext, Type> akzeptiert. Mit dem mitgelieferten DecoratorPredicateContext zum Werksdelegierten können Sie seine Expression Eigenschaft analysieren, um herauszufinden, welche IDbContextProvider<TDbContext> injiziert wird, und basierend auf dieser Information bauen Sie eine teilweise geschlossene Version von TDbContext, aber lassen Sie TCommand öffnen für Simple Injector zu ausfüllen.

Aber um ehrlich zu sein, würde ich diesen Weg nicht nehmen. Es braucht viel Arbeit, führt zu schwer verständlichem Code und deine Mitarbeiter werden dich dafür hassen.

Versuchen Sie stattdessen, stattdessen eine Liste mit IDbContextProvider<TDbContext> Instanzen in den Decorator einzufügen. Sie können das tun, indem Sie die IDbContextProvider<out TDbContext> Registrierungen sowohl separat als auch als Teil einer Sammlung unter Verwendung RegisterCollection machen.

Wenn Sie die Sammlung injizieren, kann der Decorator einfach die Änderungen auf allen DbContext Instanzen aufrufen. SaveChanges wird wirklich schnell sein, wenn es keine Änderungen gibt, also aus einer Performance-Perspektive, ich glaube nicht, dass es etwas gibt, worüber man sich Sorgen machen muss.

+0

Ja, das fasste meine Frage richtig zusammen und danke für die ausführliche Antwort! Ich habe die Sammelroute nicht berücksichtigt, aber es wird definitiv saubereren Code produzieren, also werde ich mich dafür entscheiden. –