0

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.

Babu Mannavalappil
  • 447
  • 2
  • 9
  • 27
  • Where are you calling the `Resolve` method? – Yacoub Massad Jul 11 '16 at 20:21
  • @Yacoub: I have a UnitOfWork that takes IDataContextAsync as a constructor parameter. A service layer class in turn, takes IUnitOfWork as it's Constructor argument. These interfaces are registered with Unity already. So, my assumption is all these constructor arguments are automatically resolved by Unity and I do not manually call Resolve on any of these constructor parameters. Hope this answers yours question. So, since I need to override or inject the constructor of my dbContext at runtime, is my only option to call the Resolve somewhere on the DataContext? – Babu Mannavalappil Jul 11 '16 at 22:57
  • At the end of your question you provide a code snippet that invokes `Resolve`. Are you using this code somewhere? What exactly is your question? – Yacoub Massad Jul 11 '16 at 23:00
  • No, I am not using that code. I cut and paste it from Microsoft Unity page. My question is how can I get my dbcontext connection string injected at runtime, without calling the Reolve. The only reason I don't want to call Resolve is that people are yelling that Service Locator is an antipattern. – Babu Mannavalappil Jul 11 '16 at 23:03
  • Here is a similar question that was asked today: http://stackoverflow.com/questions/38311033/dependency-injection-for-dal-with-multiple-database-strings-using-structuremap – Yacoub Massad Jul 11 '16 at 23:07
  • @Yacoub: Thanks for the similar link. That link gave me another link that Mark Seemann himself is giving some explanation about abstract factory. At the first glance I couldn't wrap my head around the Abstract factory pattern. But I think I am going to spend some time understanding this pattern. This might be the answer to my problem. Thanks for pointing this out. – Babu Mannavalappil Jul 11 '16 at 23:39
  • Do you need to switch between the databases Group1, Group2, and Group3 at design time or at runtime? What I mean is do you have one class in your service layer that always talks to Group1 and only Group1 or does your service layer pick Group1, Group2, or Group3 based on input parameters? I have a solution for both, but the runtime solution is more complex than the design time solution. – TylerOhlsen Jul 13 '16 at 02:53

1 Answers1

0

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.

Community
  • 1
  • 1
TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
  • On the surface, your sample does look like an overkill. But if it comes down it, I would rather use an overkill of a solution than abusing the DI pattern. I still have a concern. Your example still uses the Ioc Container outside the composition root. If I wanted to do that, I could just do a container.Resolve with a parameter override (I suppose). This is what I am trying to avoid. It then, becomes a service locator. You know what they say about service locators! But thanks for the sample. I will give it a shot. – Babu Mannavalappil Jul 13 '16 at 12:16
  • It does not use the container outside of the composition root. In a console app, the main method is the location for the composition root and the place you have to do resolves. You could easily add a service class that has those types I resolve as constructor parameters with no need to call to the container directly. – TylerOhlsen Jul 13 '16 at 13:20
  • Thanks. Would you be kind enough to post a sample of the service class you are referring to? Also, couple of things to keep in mind. My bootstrapper for registration runs before the user logs in. I have to resolve the user related dbcontext with the connection string after the fact. Other than calling container.Resolve with parameteroverride in possibly the login class (by then, we are not in composition root), how can I register the correct dbcontext? – Babu Mannavalappil Jul 13 '16 at 18:26
  • [Here's](https://dotnetfiddle.net/8hyqq8) an example with service/controller classes. In your bootstrap, you would need to enumerate through all possible database connections and register them all. Lines 48-50 in the sample show the use of the InjectionConstructor to specify the connection string or name. – TylerOhlsen Jul 13 '16 at 18:41
  • Thanks Tyler. I think your sample gives me some revelations and ideas. I will work it out. If you can post your example as a reply, I can mark it as accepted. (I haven't tried your sample though. But I think it should work.) Thanks again for your help. – Babu Mannavalappil Jul 13 '16 at 18:55
  • Edited answer with link to new sample. – TylerOhlsen Jul 13 '16 at 18:57