Dana Ezer
Dana Ezer

Reputation: 61

Using DbContext dynamically

I have a multiple DBs with with identical schema. In my code, I would like to query one of the DBs by a parameter, which change dynamically.

Now it looks something like this:

public void saveNewUser(User user, string type)
{
        if (type == "A")
        {
            using (var db = new AEntities())
            {
                db.Users.Add(user);
                db.SaveChanges();
            }
        }
        else
        {

            using (var db = new BEntities())
            {
                db.Users.Add(user);
                db.SaveChanges();
            }
        }

 }

Instead, I would like to do something like this:

public void saveNewUser(User user, string type)
{
        using (var db = new GeneralEntity(type))
        {
            db.Users.Add(user);
            db.SaveChanges();
        }
}

Any good ideas?

Upvotes: 2

Views: 1731

Answers (4)

Dana Ezer
Dana Ezer

Reputation: 61

What I was looking for is how to return the AEntity / BEntity dynamically, the objects already exists.

What I have done is this:

public class DbContextGeneral : DbContext
{
    public virtual Users;
}

public class AEntity : DbContextGeneral
{
    public override Users;
}
public class BEntity : DbContextGeneral
{
    public override Users;
}

In order to receive the type dynamically:

private DbContextGeneral GetDBInstance(string value)
{
    if (value == "A")
    {
        return new AEntity();
    }
    return new BEntity();
}

And then, finally:

using (var db = GetDBInstance(value))
{
    db.Users.Add(user);
    db.SaveChanges();
}

Upvotes: -1

reckface
reckface

Reputation: 5858

In my experience, I have only one DbContext to solve similar problems. I just modify the connection string at runtime. Entity Framework supports this by allowing you to call the constructor with a connection string.

I allow administrators to maintain settings as profiles, for a given model, that the application stores as a collection of encrypted SQL settings. At runtime, this list is loaded as dictionary with the profile name ("A", "B") serving as keys.

Then use the DbContext constructor that expects a connection string and pass it a built connection string based on your target db:

// the model name in the app.config connection string (any model name - Model1?)
private string BuildConnectionString(string profile)
{
    // configuration would hold the collection of settings - however you prefer to provide this
    var settings = configuration.GetConnectionSettings(profile);
    // Build the provider connection string with configurable settings
    var providerSB = new SqlConnectionStringBuilder
    {
        InitialCatalog = settings.InitialCatalog,
        DataSource = settings.DataSource,
        UserID = settings.User,
        Password = settings.Password
    };    
    var efConnection = new EntityConnectionStringBuilder();
    // or the config file based connection without provider connection string
    // var efConnection = new EntityConnectionStringBuilder(@"metadata=res://*/model1.csdl|res://*/model1.ssdl|res://*/model1.msl;provider=System.Data.SqlClient;");
    efConnection.Provider = "System.Data.SqlClient";
    efConnection.ProviderConnectionString = providerSB.ConnectionString;
    // based on whether you choose to supply the app.config connection string to the constructor
    efConnection.Metadata = "res://*/Model.DbEntities.csdl|res://*/Model.DbEntities.ssdl|res://*/Model.DbEntities.msl", model;
    return efConnection.ToString();

}

Then in your example, you will only use one DbContext:

using (var db = new DbEntities(BuildConnectionString("A")))
{
    db.Users.Add(user);
    db.SaveChanges();
}

Upvotes: 2

Igor
Igor

Reputation: 62213

DbContext has method Set<TEntity> which returns a generic DbSet<TEntity>. You can use this instead of the added properties to any override of a DbContext, ie. reference the base class DbContext in your calling code and it will not care what the actual instance is for that DbContext.

public class MyClass  {
    private DbContext _context;
    public MyClass(DbContext context){
        _context = context;
    }

    public void saveNewUser(User user)
    {
        _context.Set<User>.Add(user);
        _context.SaveChanges();
    }
}

The example above relies on DI to get the correct DbContext instance in the constructor, it does not care which specific implementation as long as it knows about the entity User later on when the method saveNewUser is called (otherwise exception will be thrown). The DI framework can also handle disposing the created DbContext or you can implement IDisposable to MyClass and dispose of DbContext in the disposing method. Alternatively you can inject a DbContext factory to create one when needed. (there are many options, just depends on your preferred framework and probably how your app is currently structured).

Assumption

I assumed that you meant that the actual underlying entities are the exact same types. If this is wrong and there are also many User types then this will not work.

Upvotes: 0

CompanyDroneFromSector7G
CompanyDroneFromSector7G

Reputation: 4517

Use something like a factory class maybe?

public void saveNewUser(User user, string type)
{
        using (var db = myclass.GetGeneralEntity(type))
        {
            db.Users.Add(user);
            db.SaveChanges();
        }
}

Upvotes: 0

Related Questions