Entity Framework Change Connection at Runtime

Entity Framework change connection at runtime

A bit late on this answer but I think there's a potential way to do this with a neat little extension method. We can take advantage of the EF convention over configuration plus a few little framework calls.

Anyway, the commented code and example usage:

extension method class:

public static class ConnectionTools
{
// all params are optional
public static void ChangeDatabase(
this DbContext source,
string initialCatalog = "",
string dataSource = "",
string userId = "",
string password = "",
bool integratedSecuity = true,
string configConnectionStringName = "")
/* this would be used if the
* connectionString name varied from
* the base EF class name */
{
try
{
// use the const name if it's not null, otherwise
// using the convention of connection string = EF contextname
// grab the type name and we're done
var configNameEf = string.IsNullOrEmpty(configConnectionStringName)
? source.GetType().Name
: configConnectionStringName;

// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString);

// init the sqlbuilder with the full EF connectionstring cargo
var sqlCnxStringBuilder = new SqlConnectionStringBuilder
(entityCnxStringBuilder.ProviderConnectionString);

// only populate parameters with values if added
if (!string.IsNullOrEmpty(initialCatalog))
sqlCnxStringBuilder.InitialCatalog = initialCatalog;
if (!string.IsNullOrEmpty(dataSource))
sqlCnxStringBuilder.DataSource = dataSource;
if (!string.IsNullOrEmpty(userId))
sqlCnxStringBuilder.UserID = userId;
if (!string.IsNullOrEmpty(password))
sqlCnxStringBuilder.Password = password;

// set the integrated security status
sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity;

// now flip the properties that were changed
source.Database.Connection.ConnectionString
= sqlCnxStringBuilder.ConnectionString;
}
catch (Exception ex)
{
// set log item if required
}
}
}

basic usage:

// assumes a connectionString name in .config of MyDbEntities
var selectedDb = new MyDbEntities();
// so only reference the changed properties
// using the object parameters by name
selectedDb.ChangeDatabase
(
initialCatalog: "name-of-another-initialcatalog",
userId: "jackthelady",
password: "nomoresecrets",
dataSource: @".\sqlexpress" // could be ip address 120.273.435.167 etc
);

I know you already have the basic functionality in place, but thought this would add a little diversity.

Change Connection String at Runtime in EF 6

You could try a static factory method (or factory class whichever you prefer) on your context which uses the connection string like so:

public class MyContext : ...
{
public MyContext(string connectionstring)
: base(connectionstring)
{
}

public static MyContext Create()
{
return new MyContext(DbAccessor.ConnectionStringForContext);
}
}

and then you can create your context like so:

using (var context = MyContext.Create())
{
}

Entity Framework - change connection string at runtime (explanation needed)

SOLUTION: This is what finally worked for me.

Controller class responsible for access to SQL Server:

public class FrequentlyAccessedQueries : Controller
{
private CATALOGEntities db = FrequentlyAccessedQueries.entities();

public static CATALOGEntities entities()
{
QueryDetails qdetails = new QueryDetails();
bool uk = qdetails.IsCountryUK;
if (uk)
{
return new CATALOGEntities("name=CATALOGEntitiesUK");
}
else
{
return new CATALOGEntities("name=CATALOGEntitiesUSA");
}
}
}

DbContext class:

public partial class CATALOGEntities : DbContext
{
public CATALOGEntities(string connectionString)
: base(connectionString)
{
}
}

web.config entries:

<connectionStrings>
<add name="CATALOGEntitiesUK" connectionString="[...]" providerName="System.Data.EntityClient" />
<add name="CATALOGEntitiesUSA" connectionString="[...]" providerName="System.Data.EntityClient" />
</connectionStrings>

asp.net core how to change connection string at run time from entity framework (after login)

IMO,you could not change the services.AddDbContext<T> at runtime.A workaround is that you add a DBContextFactory to create new dbcontext object when you login successfully.

Refer to following steps:

1.Create a DBContextFactory.cs

public static class DbContextFactory
{
public static Dictionary<string, string> ConnectionStrings { get; set; }

public static void SetConnectionString(Dictionary<string, string> connStrs)
{
ConnectionStrings = connStrs;
}

public static ApplicationDbContext Create(string connid)
{
if (!string.IsNullOrEmpty(connid))
{
var connStr = ConnectionStrings[connid];
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connStr);
return new ApplicationDbContext(optionsBuilder.Options);
}
else
{
throw new ArgumentNullException("ConnectionId");
}
}
}

2.Intialize DbContextFactory in startup Configure

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Dictionary<string, string> connStrs = new Dictionary<string, string>();
connStrs.Add("DB1", "Your connection string 1");
connStrs.Add("DB2", "Your connection string 2");
DbContextFactory.SetConnectionString(connStrs);
//other middlewares
}

3.Usage

if(status)
{
var dbContext = DbContextFactory.Create("DB2");//get the dbcontext with connection string 2
}

How to change connection string at runtime with EF and Structuremap?

I guess your EF Core DbContext looks like this:

public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}

In this case you can just create your DbContext (you don't have to use DI everywhere) and point it to database you need:

var connectionString = ...
var builder = new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(connectionString);
var context = new MyDbContext(builder.Options);
var locations = context.Locations.ToList();

Of course, you can implement somthing more sofisticated like factory class creating DbContext pointed to where you need and register this factory via DI to get it via contructor injection mechanism. The factory will have some method like:

// This is a pseudo code below
factory.CreateDbContext (.. some parameters to detect which DB to use ..)

Entity Framework 6 set connection string runtime

You are getting the Code First mode exception because you are passing a DbConnection built with the ADO.NET connection string. This connection string does not include references to metadata files, so EntityFramework does not know where to find them.

To create a DbContext with an appropriate programmatically set connection string, use the EntityConnectionStringBuilder class.

var entityBuilder = new EntityConnectionStringBuilder();

// use your ADO.NET connection string
entityBuilder.ProviderConnectionString = conString;

// Set the Metadata location.
entityBuilder.Metadata = @"res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl";
var dbContext = new DbContext(entityBuilder.ConnectionString);

Dynamically change connection string in Asp.Net Core

We have a case similar to you. What we've done is use the implementationfactory overload of the IServiceCollection in the ConfigureServices method of the Startup class, like so:

//First register a custom made db context provider
services.AddTransient<ApplicationDbContextFactory>();
//Then use implementation factory to get the one you need
services.AddTransient(provider => provider.GetService<ApplicationDbContextFactory>().CreateApplicationDbContext());

It is very difficult for me right now to implement CreateApplicationDbContext for you, because it totally depends on what you want exactly. But once you've figured that part out how you want to do it exactly, the basics of the method should look like this anyway:

public ApplicationDbContext CreateApplicationDbContext(){
//TODO Something clever to create correct ApplicationDbContext with ConnectionString you need.
}

Once this is implemented you can inject the correct ApplicationDbContext in your controller like you did in the constructor:

public MyController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

Or an action method in the controller:

public IActionResult([FromServices] ApplicationDbContext dbContext){
}

However you implement the details, the trick is that the implementation factory will build your ApplicationDbContext everytime you inject it.

Tell me if you need more help implementing this solution.

Update #1
Yuriy N. asked what's the difference between AddTransient and AddDbContext, which is a valid question... And it isn't. Let me explain.

This is not relevant for the original question.

BUT... Having said that, implementing your own 'implementation factory' (which is the most important thing to note about my answer) can in this case with entity framework be a bit more tricky than what we needed.

However, with questions like these we can nowadays luckily look at the sourcecode in GitHub, so I looked up what AddDbContext does exactly. And well... That is not really difficult. These 'add' (and 'use') extension methods are nothing more than convenience methods, remember that. So you need to add all the services that AddDbContext does, plus the options. Maybe you can even reuse AddDbContext extension method, just add your own overload with an implementation factory.

So, to come back to your question. AddDbContext does some EF specific stuff. As you can see they are going to allow you to pass a lifetime in a later release (transient, singleton). AddTransient is Asp.Net Core which allows you to add any service you need. And you need an implementation factory.

Does this make it more clear?



Related Topics



Leave a reply



Submit