I have asked this question in a different way on another thread. I probably was not clear enough explaining what I am trying to do, so I did not get a resolution or guidance to solve my problem. Since I don't have enough privileges to add or modify my original post and code, I have to repeat the question in a slightly different light. So, please don't mark this as a duplicate question.
[Edit]
First off, thank you all for trying to help me solve this issue. In spite of all your help, I still have not found an amicable solution. I am beginning to worry that I may not be as intelligent and smart as I thought. Otherwise, this should not be this hard of a problem to solve. I am probably not asking the right questions. So, I am going to present my question in yet another way.
Here are my interfaces:
public interface IDataContext : IDisposable
{
int SaveChanges();
}
public interface IDataContextAsync : IDataContext
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
Task<int> SaveChangesAsync();
}
public class DataContext : DbContext, IDataContextAsync
{
bool _disposed;
public DataContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
}
public interface IUnitOfWork : IDisposable
{
IDataContext DataContext { get; }
int SaveChanges();
void Dispose( bool disposing );
IRepository<TEntity> Repository<TEntity>() where TEntity : class, IObjectState;
void BeginTransaction( IsolationLevel isolationLevel = IsolationLevel.Unspecified );
bool Commit();
void Rollback();
}
public interface IUnitOfWorkAsync : IUnitOfWork
{
Task<int> SaveChangesAsync();
Task<int> SaveChangesAsync( CancellationToken cancellationToken );
IRepositoryAsync<TEntity> RepositoryAsync<TEntity>() where TEntity : class, IObjectState;
}
Now, my dbcontexts:
public partial class MasterDB : DataContext
{
public MasterDB( string nameOrConnectionString ) : base( nameOrConnectionString )
{
}
}
public partial class SubscriberDB : DataContext
{
public SubsDB(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
}
Here is my unit of work:
public class UnitOfWork : IUnitOfWorkAsync
{
private IDataContextAsync dataContext;
private bool disposed;
private Dictionary<string, dynamic> repositories;
public UnitOfWork( IDataContextAsync dataContext )
{
this.dataContext = dataContext;
repositories = new Dictionary<string, dynamic>();
}
public int SaveChanges()
{
return dataContext.SaveChanges();
}
// rest of the code omitted for brevity!....
}
and this is my unity bootstrapper initializer that is called at app start. I have removed other registrations for brevity.
public class PCUnityBootstrapper : UnityBootstrapper
{
public IUnityContainer RegisterAllDependencies( IUnityContainer container )
{
container.RegisterType<IUnitOfWorkAsync, UnitOfWork>( nameof( DBNames.MasterUOW ) );
container.RegisterType<IUnitOfWorkAsync, UnitOfWork>( nameof( DBNames.SubsUOW ) );
container.RegisterType<IConnectionStringProvider, ConnectionStringProvider>();
container.RegisterType( typeof( IRepositoryAsync<> ), typeof( Repository<> ) );
container.RegisterType<IDataContextAsync, MasterDB>( nameof( DBNames.MasterDB ), new ContainerControlledLifetimeManager(), new InjectionConstructor( "name=" + nameof( DBNames.MasterDB ) ) );
container.RegisterType<IDataContextAsync, SubsDB>( nameof( DBNames.SubsDB ), new ContainerControlledLifetimeManager(), new InjectionConstructor( "name=" + nameof( DBNames.SubsDB ) ) );
return container;
}
public IDataContextAsync RegisterDBatRunTime( IUnityContainer container, string namedRegistration,
string dataSource, string dbName, string userId = null, string password = null )
{
var connString = ( new ConnectionStringProvider() ).GetConnectionString( dataSource, dbName, userId, password );
container.RegisterType<DataContext>( namedRegistration,
new InjectionConstructor( "nameOrConnectionString=" + connString ) );
return container.Resolve<IDataContextAsync>( namedRegistration );
}
}
- I have a MasterDB DbContext that derives from DataContext and is for application wide configurations and also for login validations and is a static context.
- I have a SubsDB DbContext that also derives from DataContext but with a different schema. Due to business requirements, there are multiple sql databases with the SubsDB schema; each one specific to a group that the logged in user belongs to.
Now, the MasterDB can be registered and resolved without any problem since it will never change. The problem is registering and resolving the SubsDB DataContext.
The underlying Database connection is not known until the user has logged in. So, I cannot register this type at application start up call in my UnityBootstrapper even using InjectionFactory or any other factories (to my knowledge at least) because the database name in the connection string is not known).
In order to satisfy "as close to composition root as possible" rule, I have created another method RegisterDBatRunTime method in my bootstrapper. I can't say I like the way I coded the method. But when the user has finished logging in, I can call this method to register the SubsDB with the correct database. The problem here is, I have to keep a reference to my container after it was initialized, so as to register SubsDB type.
So, here are my questions (the edited ones).
I have two named registrations for my UnitOfWork so that I can inject the correct DataContext into it. But how do I inject them into the constructor of UnitOfWork? Even if I use the attribute like below, wouldn't that create a tight coupling between my UnitOfWork and the Unity Container?
public UnitOfWork([Dependency(nameof( DBNames.SubsUOW ))] IDataContextAsync dataContext ) { this.dataContext = dataContext; repositories = new Dictionary(); }
If I take this route, I can definitely check who the currently logged in user is and then change the database of the dataContext. But will this work?
Even if it does work, wouldn't I need an exact replica of another
UnitOfWork to take my DBNames.MasterDB to deal with my other schema? That goes against the DRY priciple.
I have been struggling to find a solution for the last full week but not getting anywhere. I have to say that quite a few members here have suggested quite a few solutions. Most of them seem to be viable answers. But when I begin to implement them, I find myself against new brick walls.
I am relatively new to Dependency Injection and IoC containers but have quite a few years as a developer under my belt. This is why I am beginning to worry that I am not being able to find an amicable solution to this problem. The deeper I go, the more confused I get. StackOverflow is one of the very few places where I find some code samples using Unity IoC. Other places, I can only find boilerplate code that does not seem to educate me in my unique situation. Or is my problem all that unique after all?
All I want is: implement DI the right way and avoid tight coupling between my classes and components. Resolve some of my instances using runtime information without abusing the IoC container as a service locator.
I am going crazy for the past few days on this one. So, someone please help. I am a visual person. So, some code implementing my unique scenario would greatly alleviate my pain. Thanks.