Dynamic database connectionstring based on Identity Claims

Viewed 868

I'm adding some 'tenancy' to my web application.
My web application uses 2 databases

  • Identity database
  • One tenant specific database (all same schema)

The application was built using one static connection string for the 2nd database.

My fine working AppointmentsControllers using a static connection string:

public class AppointmentsController : Controller
{
    private appDbContext _context;

    public AppointmentsController()
    {
        _context = new appDbContext();
    }

    protected override void Dispose(bool disposing)
    {
        _context.Dispose();
    }

    // Index
    public ViewResult Index()
    {
        var query = from c in _context.Appointments
                    orderby c.RegistrationDate
                    select c;
        //etc...
    }
}

To add some multi tenancy i've changed the DbContext part so i can modify the database connection. This part is working fine, it allows me to change the database name:

public class appDbContext : DbContext
{
    public DbSet<Appointments> Appointments { get; set; }

    public appDbContext(string database)
        : base(@"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=" + database + ";Integrated Security=True")
    {
    }
}

From here i get lost. It seems possible to connect the right database from my AppointmentsController constructor like this:

public class AppointmentsController : Controller
{
    private appDbContext _context;

    public AppointmentsController()
    {
        _context = new appDbContext("DbName");
    }

But i cannot retreive my Claim from identity and pass it from this constructor to the constructor like this:

public class AppointmentsController : Controller
{
    private appDbContext _context;

    public AppointmentsController()
    {
        var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var currentUser = manager.FindById(User.Identity.GetUserId());
        _context = new appDbContext(currentUser.DBName);
    }           

So my best approach that get's the job done is this, but I think it's not really clean/smooth:

public class AppointmentsController : Controller
{
    private appDbContext _context;

    public AppointmentsController()
    {
        _context = new appDbContext("");
    }

    protected override void Dispose(bool disposing)
    {
        _context.Dispose();
    }

    private string GetCurrentUserDatabaseName()
    {
        var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
        var currentUser = manager.FindById(User.Identity.GetUserId());

        return currentUser.DBName;
    }

    // Index
    public ViewResult Index()
    {
        _context = new appDbContext(GetCurrentUserDatabaseName());

        var query = from c in _context.Appointments
                    orderby c.RegistrationDate
                    select c;
     //etc
    }
}

I feel like i'm close, but how can i improve this code?

1 Answers

Add Two Connection String in Web.Config :

  <connectionStrings>
    <add name="DefaultConnection" connectionString=Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=DefaultDataBase;Integrated Security=True" providerName="System.Data.SqlClient" />     
  </connectionStrings>

Then

    public class ApplicationDbContext : DbContext //change DbContext name appDbContext by ApplicationDbContext
        {
            public DbSet<Appointments> Appointments { get; set; }

            public ApplicationDbContext(database=""): base("name=DefaultConnection")
            {
          //And/Or you can do this programmatically.
          if(!string.IsNullOrWhiteSpace(database)
             {
               this.Database.Connection.ConnectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=" + database + ";Integrated Security=True";
 //OR Database.Connection.ChangeDatabase(database);
             }
            // More Stuff.....
            }
        }

Use :

public class AppointmentsController : Controller
{
    private ApplicationDbContext _context;

    public AppointmentsController()
    {
        _context = new ApplicationDbContext("DataBasConnection");
    }
}

OR

When You need DefultDatabase then

public class AppointmentsController : Controller
{
    private ApplicationDbContext _context;

    public AppointmentsController()
    {
        _context = new ApplicationDbContext();
    }
}
Related