4

Having my own implementation of the IdentityDbContext, I would like it to be able to connect to a custom database according to the user's choice. So I have created 2 constructors for both default database and user selected database.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("Users", throwIfV1Schema: false)
    {
        //Aici am adaugat
        Configuration.ProxyCreationEnabled = true;
        Configuration.LazyLoadingEnabled = true;
    }

    public ApplicationDbContext(String connectionName)
        : base(connectionName, throwIfV1Schema: false)
    {
        //Aici am adaugat
        Configuration.ProxyCreationEnabled = true;
        Configuration.LazyLoadingEnabled = true;
    }
}

My problem is now the way I would bring the custom connectionName into the class.

The constructor is called in this method:

public static string conn;
public static ApplicationDbContext Create()
        {
            if(conn == null)
                return new ApplicationDbContext();
            else
                return new ApplicationDbContext(conn);
        }

Using session variables is impossible because in this context HttpContext.Current is null. Adding a string argument to the Create method is also impossible, because right in the Startup class, before any user selection, Owin decides on using a default database:

app.CreatePerOwinContext(() => ApplicationDbContext.Create());

Even passing an argument there would not help because it wouldn't have been chosen by the user.

What can I do about it?

Thank you very much!

Ionna
  • 223
  • 4
  • 19
  • What do you mean chosen by the user? You mean the administrator of a site where other users authenticate to the Identity instance? If not I would not see how it is possible as you do not know who the user is until after authentication which happens after you could select a connection. It would help more if you could explain what you were actually trying to achieve by doing this. – Igor Oct 25 '16 at 19:55
  • I don't know exactly what you mean, I have the same questions that @Igor has, but maybe this can help http://stackoverflow.com/questions/18297052/asp-net-identity-how-to-set-target-db – Jack1987 Oct 25 '16 at 19:56
  • This won't work since there's a single ApplicationDbContext per Request with his Owin configuration. – Dominique Alexandre Oct 25 '16 at 20:33

2 Answers2

0

I would use cookies.

Try this:

Startup.Auth.cs

app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);

ApplicationDbContext.cs

public static ApplicationDbContext Create(IdentityFactoryOptions<ApplicationDbContext> options, IOwinContext context)
{
    // Do things here.
    string choice = context.Request.Cookies...;
    // Make sure that the cookie is correct.
    return new ApplicationDbContext(connectionName);
}
Dominique Alexandre
  • 1,023
  • 10
  • 29
  • Could you give a reason as to why cookies are not good in this case? The question says "user's choice". – Dominique Alexandre Oct 25 '16 at 20:24
  • For a start cookies are not secure so will let people inject anything they want into your app. – DavidG Oct 25 '16 at 20:25
  • The question specifically says that it's the user's choice, and the cookie is the user's... And you could just filter the cookie and make the correct checks on the validity of the cookie. – Dominique Alexandre Oct 25 '16 at 20:26
  • There's a big difference between letting a user choose between a set of fixed options and letting them push anything they want into your app. – DavidG Oct 25 '16 at 20:26
  • Thank you! I'll try the method and let you know. – Ionna Oct 26 '16 at 11:13
0

What you can do is have a static class that uses a provider to produce the connection string.

You can then let the user select the connection and store it wherever it works for you. The provider will read the connection string and feed it to the context initializer.

This way you can set the provider before all this takes place. You can mock it if you want to for unit testing purposes.

I'm writing from my cellphone. I'll improve the answer with code samples when I'm in front of a desktop.

Update (I am now in front of a desktop):

The easiest way to implement this is to modify the context class by adding a constructor that takes a nameOrConnectionString parameter and having the Create() method call that constructor with a provider that gets you the connection string:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{        
    public ApplicationDbContext()
        : base("aspNetIdentity", throwIfV1Schema: false)
    {
    }

    public ApplicationDbContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {

    }

    public static ApplicationDbContext Create()
    {            
        return new ApplicationDbContext(ConnectionStringProvider.ConnectionString);
    }
}

The ConnectionStringProvider class is a static class that holds the connection string for you:

public static class ConnectionStringProvider
{
    public static string ConnectionString { get; set; }        
}

You can then set the connection string upon startup to a default and let the user change it. Upon change, store in this property.

Mind you, this is a simplistic answer meant to indicate a possible solution. Keep in mind that since the class is static, only one instance will exist and the connection string will be served to all callers so you need to account for that with concepts like dependency injection and session/user tracking.

For instance, the class could use an internal provider that is managed by a Dependency Resolver. You then let the DI container manage the scope of the provider.

JuanR
  • 7,405
  • 1
  • 19
  • 30