How would I pass a constructor parameter at runtime to my dbContext and also register them with Unity as such

两盒软妹~` 提交于 2019-12-12 05:44:33

问题


I am trying to implement all sorts of good stuff like UnitOfWork, Repository, DI. I am using Unity for DI. Here is my dilemma. I have a few (currently 3) databases with identical schema but obviously with different data for business reasons (I will call them GroupDB1, GroupDB2 and GroupDB3). I also have a Master Database (DifferentDB) that has a different schema. My dbcontext need to use different databases for different scenarios at runtime. I have no clue how to put them all to work together.

Here is my 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)
    {
        //......
    }
}

and here are my other interfaces and implementations.

    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;
}

Since I read a lot about anti-patterns I am reluctant to do

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

I am stumped. Any help is highly appreciated. Thanks.

Babu.


回答1:


I came up with an example that might be a bit over engineered, but I believe it gives you the most flexibility. You can see the entire example here. If you only want support for design time or only support for runtime, you can probably clean it up a bit.

For design time resolution, this uses an additional generics parameter as a token to identify the data store you wish to connect to. That allows you to resolve (via constructor injection) a unit of work and/or a repository that is specific to one data store.

MyService(IUnitOfWork<Group2Token> unitOfWork) { /* ... */ }

For runtime resolution, this uses a manager class to retrieve an instance of the desired unit of work with a string token.

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

The managers use Unity's built-in support for resolving all named registrations into an array. More on that can be found in this question and answer.

Note that I suggest you use HierarchicalLifetimeManager for the registration of anything that's disposable. If you use that in combination with using child containers, you will have an automatic disposal mechanism. More info in this question and answer.



来源:https://stackoverflow.com/questions/38314793/how-would-i-pass-a-constructor-parameter-at-runtime-to-my-dbcontext-and-also-reg

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!