Entity Framework changing database of existing DbContext - c#

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;
}
}

Related

Get connection string value from Microsoft.AspNetCore.TestHost.TestServer

I have a test project, i need to get the connection string value in a test class
public class EmplyeesScenarios
{
private readonly TestServer _testServer;
private readonly AppDbContext _testService;
public EmplyeesScenarios()
{
_testServer = TestServerFactory.CreateServer<TestsStartup>();
var dbOption = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer("//here i need to put the same connection string of _testServer")
.Options;
_testService = new AppDbContext(dbOption);
}
}
You can use the service collection on your test host. For example:
var config = _testServer.Host.Services.GetRequiredService<IConfigurationRoot>();
var connectionString = config.GetConnectionString("Foo");
However, you should actually be using this for all your services, so instead of trying to new up your context at all, just do:
var context = _testServer.Host.Services.GetRequiredService<AppDbContext>();
No connection string needed.
Assuming that your test server is set up properly, you should just resolve your database context from the server instance directly. Like this:
_testServer = TestServerFactory.CreateServer<TestsStartup>();
// create service scope and retrieve database context
using (var scope = _testServer.Host.Services.CreateScope())
{
var db = scope.ServiceProvider.GetService<AppDbContext>();
// ensure that the db is created for example
await db.Database.EnsureCreatedAsync();
// add test fixtures or whatever
}
Technically, you could also resolve the configuration from the test host and the read the connection string out of it but since you are doing an integration test, you should actually test the full integration and not deviate from the existing setup by creating your database context manually.

Updating EF model in new DbContext instance

What is the best way to update an entity framework model in the database with two instances of a DbContext. For example i have a simple User model which is loaded in one DbContext but saved in another:
User model = null;
using(SampleDbContext dbContext = new SampleDbContext())
{
model = dbContext.User.Find(0);
}
model.UserName = "Test-User";
using(SampleDbContext dbContext = new SampleDbContext())
{
// Here is the place i want to save the changes of the model
// without loading it again from the database and set each property
// ...
dbCotnext.SaveChanges();
}
In my case i want to write some UserManager, which has Create, Update and Delete methods. I think creating one DbContext instance for the howle manager
is no solution, because i only want to save the changes of a specific model.
I also don't want to load the models for updating again from the database and settings each value from the source instance, like:
// Update user
using(SampleDbContext dbContext = new SampleDbContext())
{
// I don't want this:
var model = dbContect.User.Find(0);
model.UserName = sourceModel.UserName;
// ...
dbCotnext.SaveChanges();
}
Maybe my problem with manager classes is very simple, but i could not find any good solution.
P.S.: My manager classes are often singleton classes.
Thank you.
You could in your second DbContext do something like this:
using (SampleDbContext dbContext = new SampleDbContext())
{
dbContext.Entry(model).State = EntityState.Modified;
dbContext.SaveChanges();
}
This would save the changes you have made to the entity to the database. However I can't remember if dbContext.Entry queries the DB for the entity.

HOW TO: dynamic connection string for entity framework

Like the title. How can I do it?
I tried something, but it doesn't work like I was expecting.
I'm using an Entity Framework model. I need to pass my connection string like parameter, so, in another file, I've written
namespace MyNamespace.Model
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class MyEntities: DbContext
{
public MyEntities(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
}
}
When I startup the app, I call this constructor in this way, so I can refer to this from anyway in the app:
public static MyEntities dbContext = new MyEntities(mdlImpostazioni.SetConnectionString());
where mdlImpostazioni.SetConnectionString() returns a string (the data are correct):
server=192.168.1.100\SVILUPPO;database=MyDB;uid=myName;pwd=111111;
When I execute this code, it seems to be all ok, but when I try to make a query like:
var query = (from r in MainWindow.dbContext.TabTipoSistema select r);
it throws an exception from here:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException(); //exception here
}
So, this is a bad way... which is the right one? (using only code C#, not from xaml)
Your approach is correct, but you need to remember the connection string for EF requires the metadata and so on. So use the EntityConnectionStringBuilder. For example:
// the model name in the app.config connection string (any model name - Model1?)
private static string GetConnectionString(string model, YourSettings settings)
{
// Build the provider connection string with configurable settings
var providerSB = new SqlConnectionStringBuilder
{
// You can also pass the sql connection string as a parameter instead of settings
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 = string.Format("res://*/Model.{0}.csdl|res://*/Model.{0}.ssdl|res://*/Model.{0}.msl", model); ;
return efConnection.ToString();
}
// Or just pass the connection string
private static string GetConnectionString(string model, string providerConnectionString)
{
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 = providerConnectionString;
// based on whether you choose to supply the app.config connection string to the constructor
efConnection.Metadata = string.Format("res://*/Model.{0}.csdl|res://*/Model.{0}.ssdl|res://*/Model.{0}.msl", model);
// Make sure the "res://*/..." matches what's already in your config file.
return efConnection.ToString();
}
EDIT
The exception you get is because when you pass a pure SQL connection string, it assumes you are working with Code first, so it calls the OnModelCreation event. When you include the MetaData section as shown above, that tells EF it's a complete EF connection string.
I believe the problem lies on the Datasource you specify. You need to add the port of the connection, e.g if your SQL Server is configured on Port 1433, try:
server=192.168.1.100,1433\SVILUPPO;database=MyDB;uid=myName;pwd=111111;
more details about connection strings you can find Here
Also I am not sure if uid and pwd are valid, better try User ID and Password:
Server=192.168.1.100,1433\SVILUPPO;Database=MyDB;User ID=myName;Password=111111;
Finally mind the case sensitivity.

Update detached objects in EF that already exist in object state manager

I'm hoping this will be an easy one but for the life of me, I cannot find the specific answer elsewhere on SO or any other site.
I have a basic repository/unit of work pattern with Entity Framework Code First. It all works smoothly except for certain cases of Update. THe problem is I have a set of Entity Framework model objects, all prefixed with "Db" which EF returns, but I then convert them to plain DataContract Model objects to pass to the Web layer to give separation of concerns. I have a basic conversion interface that just populates a WebModel object from the DataModel object, copying field by field verbatim.
So if you retrieve a DbUser object from EF with ID of 1, then convert to a User object, then convert that BACK to a DbUser object, you end up with a DbUser with ID of 1, but it is a DIFFERENT object to the one you started with, though they have the same primary key field, the actual CLR objects themselves are different.
The following works
User user;
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
}
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser);
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
repository.Update(user);
repository.save();
}
As they are using two different unit of works/contexts, so the second block has nothing in the ObjectStateManager to compare to and can just attach the detached object in the Update() methods
This, however does NOT work
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser)
repository.Update(user);
repository.save();
}
NOTE: I know logically this doesn't make much sense to convert and just convert back but go with it, I've simplified the example greatly to make it easier to put into paper, in my actual code there is a reason for doing it that way.
I get the usual error "an object with the same key already exists in the objectstatemanager...". I'm assuming because the Get() loads the object into EF and then the update sees that the object is detached, then tries to attach it and it already exists.
My Update method in my repository is as below
public override bool UpdateItem(DbUser item)
{
if (Work.Context.Entry(item).State == EntityState.Detached)
Work.Context.Users.Attach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
I made this Extension method to the DbContext to ReAttach the Entity without problems try it out:
public static void ReAttach<T>(this DbContext context, T entity) where T : class
{
var objContext = ((IObjectContextAdapter) context).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
Object foundEntity;
var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
// Detach it here to prevent side-effects
if (exists)
{
objContext.Detach(foundEntity);
}
context.Set<T>().Attach(entity);
}
Then just update your method :
public override bool UpdateItem(DbUser item)
{
Work.Context.ReAttach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
You might get a manged Entity, and again verbatim map the new DbUser's properties to the managed Object:
public override bool UpdateItem(DbUser item)
{
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
DbUser managedUser = repository.Get(item.PK);
//foreach DbUser property map the item to managedUser
managedUser.field1 = item.field1;
[..]
repository.Update(managedUser);
repository.Save();
}
}
If you set your context to AsNoTracking() this will stop aspmvc tracking the changes to the entity in memory (which is what you want anyway on the web).
_dbContext.Products.AsNoTracking().Find(id);
I would recommend you read more about this at http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application
Yash

Change Entity model database at runtime

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);

Categories

Resources