2016-07-29 30 views
0

Ich habe mich in StackOverflow nach einer Lösung für mein Problem umgesehen. Obwohl ich nicht denke, dass dies ein einzigartiges Problem ist, konnte ich keine gute Lösung finden.So wird ein genannter Unity-Registrierungstyp instanziiert

In meiner WPF-Anwendung muss ich in meinen Viewmodels einige Dienste aufrufen, um einige Daten zurückzugeben. Diese Dienste werden mit UnitOfWork injiziert, welches wiederum mit dem DbContext injiziert wird. Dieser dbcontext, der in UnitOfWork eingefügt wird, sollte basierend auf einigen Kriterien abweichen.

Ich habe Probleme, die IoC-Containerregistrierungen richtig durchzuführen und zur Laufzeit den richtigen DbContext zu injizieren. Also, wenn jemand kann bitte füllen Sie die Lücken (in der Einheit Registrierungen sowie ihre Verwendung). Ich habe einige Inline-Kommentare im folgenden Code, in denen ich Probleme habe und Hilfe brauche. Vielen Dank.

Wenn jemand meinen Registrierungscode auf die richtige Weise ersetzen und mir auch beibringen kann, wie er in meiner WPF ViewModel-Klasse verwendet wird, wäre das wirklich großartig! Vielen Dank.

Eine letzte Anmerkung: Wenn Sie in diesem Code Codierungsfehler finden, fragen Sie sich bitte nicht, wie funktioniert das überhaupt kompilieren? Der Code hier ist nicht mein richtiger Code. Um die Dinge zu vereinfachen, habe ich sie einfach aufgeschrieben. Aber es ähnelt sehr meinem echten App-Code.

public interface IDBContext{} 
public interface IUnitOfWork{} 
public interface ISomeEntityService{} 

public interface IRepository<T> where T : class 
{ T GetSingle(Expression<Func<T, bool>> predicate); } 

public class DBContext1 : IDBContext 
{ 
    public DBContext1(connString) : base(connString){} 
} 

public class DBContext2 : IDBContext 
{ 
    public DBContext2(connString) : base(connString){} 
} 


public class Repository<T> : IRepository<T> where T : class 
{ 
    private readonly IDBContext context; 
    private readonly IDbSet<T> dbSet; 

    public Repository(IDBContext ctx) 
    { 
     context = ctx; 
     dbSet = ((DbContext)context).Set<T>(); 
    } 

    public T GetSingle(Expression<Func<T, bool>> predicate) 
    { 
     return ((DbContext)context).Set<T>().SingleOrDefault(predicate); 
    } 
} 

public class UnitOfWork : IUnitOfWork 
{ 
    IDBContext ctx; 
    private Dictionary<string, dynamic> repositories; 

    public UnitOfWork(IDBContext context) 
    { 
     ctx = context; 
    } 

    public IRepository<T> Repository<T>() where T : class 
    { 
     if (repositories == null) 
      repositories = new Dictionary<string, dynamic>(); 

     var type = nameof(T); 
     if (repositories.ContainsKey(type)) 
      return (IRepository<T>)repositories[type]; 

     var repositoryType = typeof(Repository<>); 
     repositories.Add(type, Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), ctx)); 
     return repositories[type]; 
    } 

    public int SaveChanges() 
    { 
     return ctx.SaveChanges(); 
    } 
} 

public class MyUnityBootstrapper : UnityBootstrapper 
{ 
    protected override void ConfigureContainer() 
    { 
     Container.RegisterType<IDBContext, DBContext1>("Context1"); 
     Container.RegisterType<IDBContext, DBContext2>("Context2"); 
     Container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); 
     Container.RegisterType<IUnitOfWork, UnitOfWork>(); 
    } 
} 

public class SomeEntityService : ISomeEntityService 
{ 
    private IUnitOfWork uow; 

    public ConsumerService(IUnitOfWork _uow) 
    { uow = _uow; } 

    public SomeEntity GetSomeData(int id) 
    { 
     return uow.Repository<SomeEntity>().GetSingle(x => x.Id == id); 
    } 
} 

public class SomeViewModel : BindableBase 
{ 
    private readonly ISomeEntityService someService; 
    public SomeViewModel(ISomeEntityService _someService) 
    { 
     // when I call someService, I want to make sure it is using either 
     // DBContext1 or DBContext2 based on some condition I can set here. 
     // This is where I am totally stuck. 
     someService = _someService; 
    } 

    // get the repository instance with an id of 1000 
    someService.GetSomeData(1000); 
} 

/* 
    I could do something like this. But I am afraid, I am violating 
    two of the best practices recommendations. 
    1. I am creating a dependency to my IoC Container here. 
    2. I am using the container as a Service Locator 
*/ 
public class SomeViewModel : BindableBase 
{ 
    private readonly ISomeEntityService someService; 
    public SomeViewModel() 
    { 
     var container = SomeHowGetTheContainer(); 
     /* 
      1. Call Container.Resolve<IDBContext>(with the required context); 
      2. Use the retrieved context to inject into the UnitOfWork 
      3. Use the retrieved UnitOfWork to inject into the service 

      But that would be like throwing everything about best practices to the wind! 
     */ 
     someService = container.Resolve<ISomeEntityService>(/*do some magic here to get the right context*/) 
    } 

    // get the repository instance with an id of 1000 
    someService.GetSomeData(1000); 
} 
+0

Ich würde den Aufruf an den Container in einer abstrakten Factory-Implementierung ausblenden. Es ist in Ordnung, wenn Fabriken auf den Container angewiesen sind. – Haukinger

+0

@Haukinger: können Sie bitte einen Code einreichen, um mir zu zeigen, wie in meinem Szenario? Das würde helfen. Vielen Dank. –

Antwort

0

eine Fabrik wie diese hinzufügen, die Ihre ISomeEntityService löst:

public MySomeEntityServiceFactory 
{ 
    public MySomeEntityServiceFactory(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public ISomeEntityService CreateSomeEntityService(bool condition) 
    { 
     return _container.Resolve<ISomeEntityService>(condition ? "VariantA" : "VariantB"); 
    } 

    private readonly IUnityContainer _container; 
} 

und fügen Sie zwei Namen Bindungen wie:

_container.RegisterType<ISomeEntityService, SomeEntityService>("VariantA", new InjectionConstructor(new ResolvedParameter<IDBContext>("VariantA"))); 
_container.RegisterType<ISomeEntityService, SomeEntityService>("VariantB", new InjectionConstructor(new ResolvedParameter<IDBContext>("VariantB"))); 

Für IUnitOfWork Sie eine ähnliche Fabrik hinzufügen können, die aufgelöst wird Arbeitseinheit, und rufen Sie es SomeEntityService 's Konstruktor in der IDBContext ...

übergeben

Diese Fabriken sind zusätzliche Abhängigkeiten selbst, btw ...

+0

Danke! Lass mich sehen, ob ich es verstehe. Mein ** ViewModel ** erhält einen Konstruktorparameter ISomeEntityServiceFactory anstelle von ISomeService. Im ViewModel-Konstruktor rufe ich dann die 'factory.CreateService'-Methode auf und übergibt den entsprechenden Parameter, um den richtigen Dienst zu erhalten. Gleichermaßen erhält UnitOfWork eine 'IContextFactory' als Parameter und diese Factory wird den entsprechenden DbContext erstellen. Hab ich recht? Lassen Sie mich es versuchen und Sie so schnell wie möglich über das Ergebnis informieren. Danke noch einmal. –

+0

Dies ist keine Factory, sondern ein ServiceLocator-Muster, das von manchen als Anti-Pattern betrachtet wird. Siehe [Mark Seemann] (http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactory oderServiceLocator/) Artikel zu diesem Thema. – Michael

+0

Eine Fabrik in sich ist eine Art Service-Locator, könnte man argumentieren. Eine Abhängigkeit von einer Fabrik ist jedoch eine klare Aussage, dass man vom Produkt abhängt, während eine Abhängigkeit von einem Service Locator nur Abhängigkeiten verbirgt. Sicher, je nach Behälter ist das nicht nett, aber für Fabriken ist das ok. Was sollten sie sonst noch für die Erstellung von Produkten verwenden - "neu"? 'Activator.CreateInstance'? Beide sind dem Container deutlich unterlegen, und die Fabrik wird den Container nicht dazu benutzen, etwas zu verstecken, weil die Abhängigkeiten des Produktes selbst im Produktkonstrukteur klar angegeben sind. – Haukinger