How do I switch the database that my entity model is connected to at runtime?
If I have a training database and a production database, for example, how can I make my application switch between the two by changing a setting in the application.
Justin Pihony has the correct answer.
If you want to access both databases at the same time (switch back and forth) instead of changing config and restarting app.....then you have two settings one for Train and one for Prod then you do your context like so:
string training = ConfigurationManager.ConnectionStrings["Train"].ToString();
string production = ConfigurationManager.ConnectionStrings["Prod"].ToString();
.....
EFContext context = null;
if (InTraining)
context = new EfContext(training);
else
context = new EfContext(production);
Usually this is done by a config file setting. Here is the MSDN on EF connection strings and here is some more info on it, basically saying it should be in your app.config
And, if you want something from the code, here is a code project:
string connectionString = new System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"]);
System.Data.SqlClient.SqlConnectionStringBuilder scsb = new System.Data.SqlClient.SqlConnectionStringBuilder(connectionString);
EntityConnectionStringBuilder ecb = new EntityConnectionStringBuilder();
ecb.Metadata = "res://*/Sample.csdl|res://*/Sample.ssdl|res://*/Sample.msl";
ecb.Provider = "System.Data.SqlClient";
ecb.ProviderConnectionString = scsb.ConnectionString;
dataContext = new SampleEntities(ecb.ConnectionString);
Related
I have a piece of code (.NET Framework 4.5.2, Enterprise Library 5.0.505.0) where I need to connect to a SQL Server database. However, the DB name may keep changing depending on user's requirement. So, I have the following piece of code to write the connection string dynamically to the app.config file.
public void CreateNewConnectionStringInConfig(string initialCatalog)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = ConfigurationManager.AppSettings["Data Source"];
builder.PersistSecurityInfo = true;
builder.InitialCatalog = initialCatalog; //This is the DB name
builder.UserID = ConfigurationManager.AppSettings["User ID"];
builder.Password = ConfigurationManager.AppSettings["Password"];
// Get the application configuration file.
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Create a connection string element.
ConnectionStringSettings csSettings = new ConnectionStringSettings("UserSelectedConnectionString", builder.ConnectionString, "System.Data.SqlClient");
// Get the connection strings section.
ConnectionStringsSection csSection = config.ConnectionStrings;
// Add the new element.
csSection.ConnectionStrings.Add(csSettings);
// Save the configuration file.
config.Save(ConfigurationSaveMode.Modified);
// Refresh the section so the new configuration can be re-read
ConfigurationManager.RefreshSection("connectionStrings");
}
I checked and the connection string is getting created fine in the vshost.exe.Config file while debugging. However, when I am trying to create the Database object, I am getting an error. The code used to create the DB object is as shown below.
public class MyDac
{
private readonly Database _db;
public MyDac()
{
DatabaseProviderFactory factory = new DatabaseProviderFactory();
_db = factory.Create("UserSelectedConnectionString");
}
}
I am getting the following error while trying to create the _db object.
Activation error occured while trying to get instance of type Database, key "UserSelectedConnectionString"
Microsoft.Practices.ServiceLocation.ActivationException: Activation error occured while trying to get instance of type Database, key "UserSelectedConnectionString" ---> Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "Microsoft.Practices.EnterpriseLibrary.Data.Database", name = "UserSelectedConnectionString".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The type Database does not have an accessible constructor.
Things that I have tried:
1) Upgrading the Enterprise Library version to 6.0.0.0 resolves the issue but that is not an option for me. I have to keep it to version 5.0.505.0.
2) When I hard code the connection string in the App.config file from before hand (rather than writing it during run time), the app works fine. However, I can't do that in real life because the database name will keep on changing.
Any help will be much appreciated. Thanks!
I changed my code as shown below and that started working. However, I do not have any explanation for it:
public class MyDac
{
private readonly Database _db;
public MyDac()
{
_db = new SqlDatabase("UserSelectedConnectionString");
}
}
I'll start by asking am I right in thinking that in the image below:
the 'TABLE=CLOASEUCDBA.T_BASIC_POLICY' is not part of the connection string? in fact it is the source table name?
I'm looking to alter this to another linked table on the same database. The connection string should there be the same and the name that appears in ACCESS should be the same. The only difference should be under the hood it is actually referencing another table and of course if you open the table it will contain different fields and data.
my code for far to do this is:
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\xxxx\Documents\Test.accdb");
foreach (TableDef tbd in db.TableDefs)
{
if (tbd.Name.Contains("CLOASEUCDBA_T_BASIC_POLICY"))
{
tbd.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
}
}
db.Close();
However I'm getting a big fat COMException "Cannot set this property once the object is part of a collection.". I'm not sure exactly why and all the examples I can find online are all written in VB/VBA and I only have very very limited exposure to this. Any help is appreciated.
EDIT:
I have tried to go a different route with no futher success using the code:
if (tbd.Name.Contains("CLOASEUCDBA_T_BASIC_POLICY"))
{
var newtable = db.CreateTableDef("this is a new table");
newtable.Name = "new table";
newtable.Connect = tbd.Connect;
newtable.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
db.TableDefs.Append(newtable);
//tbd.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
}
In this case I get the error "ODBC--call failed."
Since we're not allowed to change the SourceTableName of a TableDef object that already exists in the TableDefs collection we need to create a new TableDef object, .Delete the old one, and then .Append the new one:
// This code requires the following COM reference in your project:
//
// Microsoft Office 14.0 Access Database Engine Object Library
//
// and the declaration
//
// using Microsoft.Office.Interop.Access.Dao;
//
// at the top of the class file
string tableDefName = "CLOASEUCDBA_T_BASIC_POLICY";
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\xxxx\Documents\Test.accdb");
var tbdOld = db.TableDefs[tableDefName];
var tbdNew = db.CreateTableDef(tableDefName);
tbdNew.Connect = tbdOld.Connect;
tbdNew.SourceTableName = "CLOASEUCDBA_T_BILLING_INFORMATION";
db.TableDefs.Delete(tableDefName); // remove the old TableDef ...
db.TableDefs.Append(tbdNew); // ... and append the new one
db.Close();
I have the following code somewhere in the application. the code goes like this:
Hyperion.Data.MCQEntities _model = null;
const string K_MODEL = #"res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;";
public Engine(string cnnstr)
{
//string connection =(new ConnectionStringBuilder(cnnstr)).ToString();
string connectionString = new System.Data.EntityClient.EntityConnectionStringBuilder
{
Metadata = K_MODEL, //"res://*",
Provider = "System.Data.SqlClient",
ProviderConnectionString = cnnstr,
}.ConnectionString;
_model = new Data.MCQEntities(connectionString);
_model.Connection.Open();
}
the problem I do not understand is that when I use Metadata = "res://*" it works but when I use Metadata=K_MODEL it does not work at all. what could be the issue?
res//* tells EF that metadata is embedded in the assembly as a resource. If you skip it EF is looking for file on the disk. The default build action for edmx is to embed artifacts in the assembly so if you have not changed this files are not on disk and EF cannot find them.
If I have the following code (for example, in the constructor of my repository):
var db = new MyDbContext();
var entity = db.Set<Customer>();
Then later I do:
db.Database.Connection.ConnectionString = mySQLconnectionstring;
Do I need to 're-set' the entity?
Bad idea. You should create new context instance:
var db1 = new MyDbContext("connstr1");
var db2 = new MyDbContext("connstr2");
Otherwise you'll get more difficulties, than benefits you're supposing (if this ever possible). Note, that every context instance keeps local cache of materialized entities and tracks their changes.
Since the model is the same, model building (which is most significant performance hit in EF) will happen just once. I can't imagine, what else could force you to re-use context instances.
if you want to use same context for multiple database, you can do that.
one way is changing connection string of context in the memory.
before changing db that you want to use. Call this piece of code:
var connStr="YOUR_NEW_DB_CONNECTION_STRING_HERE";
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["YOUR_DB_CONNECTION_STRING_NAME_EG_CONN_STR"].ConnectionString = connStr;
connectionStringsSection.ConnectionStrings["YOUR_DB_CONNECTION_STRING_NAME_EG_CONN_STR"].ProviderName = "System.Data.EntityClient";
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
Write new class file and add this method to your context
public partial class YourEntitities
{
public void SetDatabase(string dataBase)
{
string connString = Database.Connection.ConnectionString;
Regex rgx = new Regex(#"(?<=initial catalog=)\w+");
string newconnString = rgx.Replace(connString, dataBase);
//string = connString.Replace("{database}", dataBase);
Database.Connection.ConnectionString = newconnString;
}
}
I decided to move Entity Connection String from app.config to code. However after setting it up like this:
public static string GetConnectionString() {
string connection = "";
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
sqlBuilder.DataSource = dbServer;
sqlBuilder.InitialCatalog = dbInitialCatalog;
sqlBuilder.IntegratedSecurity = false;
sqlBuilder.UserID = dbUserName;
sqlBuilder.Password = dbPasswWord;
sqlBuilder.MultipleActiveResultSets = true;
EntityConnectionStringBuilder entity = new EntityConnectionStringBuilder();
// entity.Name = "EntityBazaCRM";
entity.Metadata = #"res://*/Data.System.csdl|res://*/Data.System.ssdl|res://*/Data.System.msl";
entity.Provider = "System.Data.SqlClient";
entity.ProviderConnectionString = sqlBuilder.ToString();
connection = entity.ToString();
return connection;
}
I have an exception thrown Unable to load the specified metadata resource. in .Designer.cs.
/// <summary>
/// Initialize a new EntityBazaCRM object.
/// </summary>
public EntityBazaCRM(string connectionString) : base(connectionString, "EntityBazaCRM")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
If I define .Name inside my Entity creator it throws another exception
"Other keywords are not allowed when the 'Name' keyword is specified." (System.ArgumentException) Exception Message = "Other keywords are not allowed when the 'Name' keyword is specified.", Exception Type = "System.ArgumentException"
I know I'm missing something that I have to change so that self generated code uses new connection string but where to look for it?
After reading this answers article and this blog I changed:
entity.Metadata = #"res://*/Data.System.csdl|res://*/Data.System.ssdl|res://*/Data.System.msl";
To:
entity.Metadata = "res://*/";
And it works :-)
I upgraded to the new csproj format(Visual studio 2017 with simple format) after that I started getting this error. The csproj has a feature where you don't need to include each file instead it includes all the relevant files under the folder by default so the entity framework files are treated same way so those are not embedded into the assembly by default.
I need to go and manually change the build action of my edml file(edmx in case of Microsoft entity framework) to 'DevartEntityDeploy' (I hope it is EntityDeploy for Microsoft Entity framework)and build it which solved my problem