2016-07-29 20 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

相关问题