依赖注入-如何有条件地实例化命名的Unity注册类型



我在StackOverflow上四处寻找问题的解决方案。虽然我不认为这是一个独特的问题,但我一直没能找到一个好的解决方案。

在我的WPF应用程序中,在我的视图模型中,我需要调用一些服务来返回一些数据。这些服务被注入UnitOfWork,而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 );
}

添加一个像这样的工厂来解决您的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,您可以添加一个解析工作单元的类似工厂,并在传递IDBContextSomeEntityService的构造函数中调用它。。。

这些工厂本身就是额外的依赖项,顺便说一句…

最新更新