0

Ich versuche, alle Arten von guten Sachen wie UnitOfWork, Repository, DI zu implementieren. Ich verwende Unity für DI. Hier ist mein Dilemma. Ich habe ein paar (derzeit 3) Datenbanken mit identischem Schema, aber offensichtlich mit anderen Daten aus geschäftlichen Gründen (ich werde sie GroupDB1, GroupDB2 und GroupDB3 nennen). Ich habe auch eine Master-Datenbank (DifferentDB), die ein anderes Schema hat. Mein dbcontext muss zur Laufzeit verschiedene Datenbanken für verschiedene Szenarien verwenden. Ich habe keine Ahnung, wie sie alle zusammen arbeiten sollen.Wie würde ich einen Konstruktor Parameter zur Laufzeit an meine dbContext übergeben und sie auch mit Unity als solche

Hier ist mein dbContexts

public partial class GroupDB2 : DataContext 
{ 
    public GroupDB2() : base("name=GroupDB2") 
    { 
    } 
    public IDbSet<T> Set<T>() where T : EntityBase { return base.Set<T>(); } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     //...... 
    } 
} 

public partial class MasterDB : DataContext 
{ 
    public MasterDB() : base("name=MasterDB") 
    { 
    } 
    public IDbSet<T> Set<T>() where T : EntityBase { return base.Set<T>(); } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     //...... 
    } 
} 

und hier sind meine andere Schnittstellen und Implementierungen.

public class DataContext : DbContext, IDataContextAsync 
{ 
    private readonly Guid _instanceId; 
    bool _disposed; 

    public DataContext(string nameOrConnectionString) : base(nameOrConnectionString) 
    { 
     _instanceId = Guid.NewGuid(); 
     //Configuration.LazyLoadingEnabled = false; 
     //Configuration.ProxyCreationEnabled = false; 
    } 
} 

public interface IDataContext : IDisposable 
{ 
    int SaveChanges(); 
} 


public interface IDataContextAsync : IDataContext 
{ 
    Task<int> SaveChangesAsync(CancellationToken cancellationToken); 
    Task<int> SaveChangesAsync(); 
} 

public interface IRepository<T> where T : class 
{ 
    IDataContextAsync Context { get; } 
    IDbSet<T> DbSet { get; } 
    void Add(T entity); 
    void Delete(T entity); 
    void Delete(dynamic id); 
    T FindOne(Expression<Func<T, bool>> predicate); 
    IQueryable<T> FindBy(Expression<Func<T, bool>> predicate); 
    IQueryable<T> GetAll(); 
    void Update(T entity); 
} 

public interface IRepositoryAsync<TEntity> : IRepository<TEntity> where TEntity : class 
{ 
    Task<TEntity> FindAsync(params object[] keyValues); 
    Task<TEntity> FindAsync(CancellationToken cancellationToken, params object[] keyValues); 
    Task<bool> DeleteAsync(params object[] keyValues); 
    Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues); 
} 

public static IUnityContainer InitializeContainer(IUnityContainer _container) 
{ 
    container = _container; 

    .... 
    .... 

    container.RegisterType<IDataContextAsync, DataContext>(new InjectionConstructor("name=MasterDB")); 
    container.RegisterType<IUnitOfWorkAsync, UnitOfWork>();// ("Async"); 

    // Here is where I have no clue how do I register and resolve the correct entity context based on some conditions 
    // Like ConnectionStringService.GetConnectionString(for some condition); 

    //container.RegisterType<IDataContextAsync, DataContext>("GroupDB", new InjectionConstructor(xxxxxx)); 
    //container.RegisterType<IDataContextAsync, DataContext>("DifferentDB", new InjectionConstructor(yyyyyy)); 

    .... 
    .... 

    return container; 
} 

Da ich viel über anti-Muster lesen bin ich nur ungern tun

var result = container.Resolve<MyObject>(
    new ParameterOverride("x", ExpectedValue) 
     .OnType<MyOtherObject>()); 

Ich bin ratlos. Jede Hilfe wird sehr geschätzt. Vielen Dank.

Babu.

+0

Wo rufst du die 'Resolve' Methode an? –

+0

@Yacoub: Ich habe eine UnitOfWork, die IDataContextAsync als Konstruktorparameter verwendet. Eine Serviceschichtklasse wiederum verwendet IUnitOfWork als Konstruktorargument. Diese Schnittstellen sind bereits bei Unity registriert. Also, meine Annahme ist, dass all diese Konstruktorargumente automatisch von Unity aufgelöst werden und ich Resolve nicht manuell auf irgendeinem dieser Konstruktorparameter aufrufe. Hoffe, das beantwortet deine Frage. Also, da ich den Konstruktor von meinem dbContext zur Laufzeit überschreiben oder injizieren muss, ist meine einzige Option, die Resolve irgendwo auf dem DataContext aufzurufen? –

+0

Am Ende Ihrer Frage geben Sie ein Code-Snippet an, das 'Resolve' aufruft. Benutzt du diesen Code irgendwo? Was genau ist deine Frage? –

Antwort

0

Ich habe ein Beispiel gefunden, das vielleicht ein bisschen überentwickelt ist, aber ich glaube, es gibt Ihnen die größte Flexibilität. Sie können das gesamte Beispiel here sehen. Wenn Sie nur Unterstützung für die Entwurfszeit oder nur Unterstützung für die Laufzeit benötigen, können Sie es wahrscheinlich etwas aufräumen.

Für die Entwurfszeitauflösung verwendet dies einen zusätzlichen Generics-Parameter als Token, um den Datenspeicher zu identifizieren, mit dem Sie eine Verbindung herstellen möchten. Auf diese Weise können Sie (über die Konstruktorinjektion) eine Arbeitseinheit und/oder ein Repository auflösen, das für einen Datenspeicher spezifisch ist.

Zur Laufzeitauflösung verwendet dies eine Manager-Klasse, um eine Instanz der gewünschten Arbeitseinheit mit einem Zeichenfolge-Token abzurufen.

MyService(IUnitOfWorkManager unitOfWorkManager) 
{ 
    _unitOfWork = unitOfWorkManager.GetUnitOfWork("Group2"); 
} 

Die Manager verwenden Unity integrierte Unterstützung für die Lösung aller namens Registrierungen in einem Array. Mehr dazu finden Sie in dieser question and answer.

Beachten Sie, dass ich vorschlage, dass Sie HierarchicalLifetimeManager für die Registrierung von allem verwenden, was wegwerfbar ist. Wenn Sie dies in Kombination mit der Verwendung von Kindercontainern verwenden, verfügen Sie über einen automatischen Entsorgungsmechanismus. Weitere Informationen in diesem question and answer.

+0

Auf der Oberfläche sieht Ihre Probe wie ein Overkill aus. Aber wenn es darauf ankommt, würde ich lieber einen Overkill einer Lösung verwenden, als das DI-Muster zu missbrauchen. Ich habe immer noch eine Sorge. In Ihrem Beispiel wird der Ioc-Container weiterhin außerhalb des Kompositionswurzels verwendet. Wenn ich das machen wollte, könnte ich einfach einen Container machen. Mit einem Parameter-Override resolvieren (nehme ich an). Das versuche ich zu vermeiden. Es wird dann ein Service Locator. Sie wissen, was sie über Service Locators sagen! Aber danke für die Probe. Ich werde es versuchen. –

+0

Es verwendet den Container nicht außerhalb des Kompositionswurzels. In einer Konsolenanwendung ist die Hauptmethode der Speicherort für den Kompositionswurzel und der Ort, den Sie erledigen müssen. Sie könnten problemlos eine Serviceklasse hinzufügen, die diese Typen als Konstruktorparameter auflöst, ohne den Container direkt aufrufen zu müssen. – TylerOhlsen

+0

Danke. Wären Sie so freundlich, ein Beispiel für die Serviceklasse zu veröffentlichen, auf die Sie sich beziehen? Außerdem sollten Sie einige Dinge beachten. Mein Bootstrapper für die Registrierung wird ausgeführt, bevor sich der Benutzer anmeldet. Ich muss den benutzerbezogenen dbcontext mit der Verbindungszeichenfolge nach der Tat auflösen. Anders als das Aufrufen von container.Resolve mit Parameteroverride in möglicherweise der Login-Klasse (bis dahin sind wir nicht im Kompositionsstamm), wie kann ich den richtigen dbcontext registrieren? –