I have a business ASP.NET MVC5 application where each customer has his own database. I want to use EF6 and Ninject for DI. For login I'm using ASP.NET Identity.
For each user exists a UserClaim where the name of the database is specified:
and so on... This means it is one web-application with a "shared" database for user authentication and on the other side each customers has his own database - all databases are located on the same database server (MS SQL Server).
The user need to login in, after login he should receive data from his personal database (specified in the UserClaim-Table).
For Ninject I think I have to something like this
private void AddBindings() {
kernel.Bind<EFDBContext>().ToMethod(c => new EFDBContext("db_name"));
}
But how would I get the UserClaim into the bindings? (I don't want to use a Session, because sessions can get lost).
And what steps after the bindings are necessary?
For example at the AccountRepository the EFDBContext expects the "db_name" > but how would I get it there?
public class AccountRepository : IAccountRepository {
private EFDBContext context = new EFDBContext("db_name");
}
And finally I can change the connection string inside of this class??
public class EFDBContext : DbContext {
public EFDBContext(string db_name) : base("EFDBContext") {
}
}
UPDATE AFTER @Hooman Bahreini ANSWER
NinjectDependencieResolver.cs
private void AddBindings() {
kernel.Bind<ICustomerRepository>().To<CustomerRepository>().WithConstructorArgument("http_current_context", HttpContext.Current);
}
CustomerRepository.cs
public class CustomerRepository : ICustomerRepository {
private CustomerDBContext context;
public CustomerRepository(HttpContext httpContext) {
string db_name = "";
var claimValue = ((ClaimsPrincipal)HttpContext.Current.User).Claims.FirstOrDefault(c => c.Type == "db_name");
if(claimValue != null) {
db_name = claimValue.Value.ToString();
}
context = new CustomerDBContext(db_name);
}
public IEnumerable<Test> Tests {
get { return context.Test; }
}
}
DB-Context-File
public class CustomerDBContext : DbContext {
public CustomerDBContext(string db_name) : base("CustomerDBContext") {
string temp_connection = Database.Connection.ConnectionString.Replace(";Initial Catalog=;", ";Initial Catalog=" + db_name + ";");
Database.Connection.ConnectionString = temp_connection;
}
public DbSet<Test> Test { get; set; }
}
You can access user claims from HttpContext
:
var claimValue = ((ClaimsPrincipal)HttpContext.Current.User)
.Claims
.FirstOrDefault(c => c.Type == "db_name");
For your ninject code, you can create an extension method for HttpContext
:
public static HttpcontextExtensions
{
public static string GetDbName(this HttpContext context)
{
return ((ClaimsPrincipal)context.Current.User)
.Claims
.FirstOrDefault(c => c.Type == "db_name");
}
}
And use the following ninject binding:
kernel.Bind<ICustomerRepository>()
.To<CustomerRepository>()
.WithConstructorArgument("db_name", HttpContext.GetDbName());
See this document for more info about accessing HttpContext
in ninject.
In your example, CustomerRepository
has a dependency on HttpContext
, this is not a good design. CustomerRepository
requires a db-name, and that's what should be passed in the constructor. Related to this is Nikola’s 4th law of IoC
Every constructor of a class being resolved should not have any implementation other than accepting a set of its own dependencies.
To give you an example, you don't have any HttpContext
in your test project, which makes unit testing CustomerRepository
complicated.
P.S. I don't know your design, but maybe getting db-name from HttpContext
is not an ideal solution... user may logout or clear their browser history and you will loose your db-name.