Dynamic connection string in Entity Framework
GOAL using Database-First Paradigm (not Code-First) for deployed wpf production application with unique databases for end users:
1) Let the EntityFramework use the connection string defined at runtime.
2) Don't deploy different app.config files.
Attempt made:
1) Overload the constructor - while successful, this solution is undesirable as it leaves the door open for developers to make mistakes, makes unit testing difficult.
2) Attempting to change the factory connection / context - threw an exception.
3) Changing the default constructor - may be successful, this solution is undesirable as the default constructor is autogenerated.
4) An attempt to modify ConfigurationSettings is an Exception, it is read-only.
5) You have a client-side deployment of app.config - while plausible, this solution is not desirable as it requires a rewrite of our deployment mechanism.
reference
EDIT: Some code related to the first item we tried (constructor overloading):
public partial class DatabaseContext
{
public DatabaseContext(EntityConnection con)
: base(con, true)
{
}
}
public static class DbContextHelper
{
public static string ConnectionString { get; set; }
public static CounterpartDatabaseContext GetDbContext()
{
EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder
{
Provider = "System.Data.SqlClient",
ProviderConnectionString = ConnectionString,
Metadata = @"res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl"
};
EntityConnection con = new EntityConnection(builder.ToString());
return new DatabaseContext(con);
}
}
Using:
public void SomeMethod()
{
using(DatabaseContext db = DbContextHelper.GetDbContext())
{
// db things
}
}
EDIT code for adding a connection string with a config manager:
public MainWindow()
{
InitializeComponent();
ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings("DatabaseContext", @"metadata=res://*/DatabaseContext.csdl|res://*/DatabaseContext.ssdl|res://*/DatabaseContext.msl;provider=System.Data.SqlClient;provider connection string="data source=sqldev;initial catalog=Dev;persist security info=True;user id=user;password=password;MultipleActiveResultSets=True;App=EntityFramework"", "System.Data.EntityClient"));
}
the config manager code just throws an exception, so no code should be specified after that.
source to share
The generated DatabaseContext class is partial. With, partial
you can add code to another file (just remember the keyword partial
) and still need to regenerate everything. The generator will only overwrite the created file, all other files with additional additions to this partial class will not evaporate. No problem with generating generated and handwritten parts there.
Also, the generated class is not sealed
. You can inherit it. So instead of using it directly, DatabaseContext
you can try inheriting from it and start using the derived class. This derived class does not inherit constructors, but it does inherit all other publicly important things. Then you will be able to provide your own constructor, even the default one, which will call the parameterized base class ctor. Actually, I haven't tried this, but it looks simple and might work.
What I'm suggesting is not using DbContextHelper.GetContext()
(which is clearly static ) (which you think developers might mistakenly use or forget) but passing into your own DbContext class.
In a project where you have EDMX and generated context DatabaseContext
add the file with
public partial class DatabaseContext
{
protected DatabaseContext(string nameOrConnstring) : base(nameOrConnstring) { }
}
it will add a new overload, it will expose the basic DbContext constructor that accepts a connstring.
Then add another file:
public class ANewContext : DatabaseContext
{
public ANewContext() : base(DbContextHelper.FindMyConnectionString()){ }
}
that's all. Since your helper was static in some way, then we can call it that. Just change it to return the connstring props that it should have defined anyway.
Now rename the classes:
DatabaseContext -> InternalDatabaseContextDontUseMe
ANewContext -> DatabaseContext
or something like that, and I'm sure no one will ever be confused about which one should be used throughout. Using:
public void SomeMethod()
{
using(var db = new DatabaseContext()) // that ANewContext after renaming
{
...
}
}
With partial
in, InternalDatabaseContextDontUseMe
you can restore the model, and the additional added ctor will not be deleted. With one additional level of inheritance, the autogenerated default constructor will be hidden, and developers using a derived class won't be able to accidentally call it, they'll get a new default ctor that will do what they want.
If you're really curious about what I've found digging into the source of EF, LazyContext, Factoryories, Resolvers, etc., check out this fine article . I put everything in there that I could remember today, and although somewhat chaotic, it might help you if you like digging and decompiling. Especially EntityConnection-DbProviderFactory-Resolvers is mentioned at the end.
source to share