2016-07-29 64 views
0

我已經瀏覽了StackOverflow以解決我的問題。雖然我不認爲這是一個獨特的問題,但我一直無法找到一個好的解決方案。如何有條件地實例化一個命名的Unity註冊類型

在我的WPF應用程序中,在我的viewmodels中,我需要調用一些服務來返回一些數據。這些服務被注入UnitOfWork,後者又被注入了DbContext。這個注入UnitOfWork的dbcontext應該根據一些標準而有所不同。

我在正確地做IoC容器註冊並在運行時注入正確的DbContext時遇到了問題。所以,如果有人可以填寫空白(在統一註冊以及它的用法)。我在下面的代碼中有一些內聯註釋,在這裏我遇到了麻煩並且需要幫助。謝謝。

如果有人能以正確的方式替換我的註冊碼,並教育我如何在我的WPF ViewModel類中使用它,那真是太棒了!謝謝。

最後一個注意事項:如果您在此代碼中發現編碼錯誤,請不要開始想知道如何編譯?這裏的代碼不是我真正的代碼。爲了簡化事情,我只是寫了起來。但它確實非常類似於我的真實應用程序代碼。

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

我會在抽象工廠實現中隱藏對容器的調用。工廠依賴容器是可以的。 – Haukinger

+0

@Haukinger:在我的場景中,您可以請包括一些代碼來告訴我如何?這將有所幫助。謝謝。 –

回答

0

添加一個工廠這樣能解決您的ISomeEntityService

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

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

    private readonly IUnityContainer _container; 
} 

,並添加兩個命名綁定,如:

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

對於IUnitOfWork,你可以添加一個類似的工廠能解決工作單位,並將其稱爲SomeEntityService的構造函數傳入IDBContext ...

這些工廠是自己的附加依賴關係,btw ...

+0

謝謝!讓我看看我是否得到它。 My ** ViewModel **將接收構造函數參數ISomeEntityServiceFactory而不是ISomeService。在ViewModel構造函數中,我將調用'factory.CreateService'方法傳遞適當的參數以獲得正確的服務。同樣,UnitOfWork將接收一個'IContextFactory'作爲參數,該工廠將創建適當的DbContext。我對麼?讓我試試看,並儘快讓你知道結果。再次感謝。 –

+0

這不是一個工廠,而是一個ServiceLocator模式,它被認爲是一種反模式。參見[Mark Seemann](http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryorServiceLocator/)關於這個主題的文章。 – Michael

+0

自己的工廠就是一種服務定位器,可以爭論。然而,對工廠的依賴是一個明確的說法,即依賴於產品,而對服務定位器的依賴只是隱藏了依賴關係。當然,取決於容器不好,但對工廠來說沒關係。他們還應該使用什麼來創造產品 - 「新」? 'Activator.CreateInstance'?兩者都明顯不如容器,並且工廠不會使用容器來隱藏任何東西,因爲產品本身的依賴性在產品構造者中明確地陳述。 – Haukinger