Error passing existing connections to DbContext constructor when using Database-First - c#

I'm trying to create a class to perform work on the database and have the need (or preference) to use a combination of DbContext and good old fashioned ADO. Why, well EF is great for simplifying a great deal of code but ADO still has many uses for more complex methods that EF cannot yet handle.
This link on MSDN states that I can pass an existing SqlConnection to my context as follows:
using (var conn = new SqlConnection("..."))
{
conn.Open();
using (var context = new SampleContext(conn, contextOwnsConnection: false))
{
// Do Something
}
}
Now I'm using Database-First so this constructor doesn't appear as standard. I therefore created a new Partial Class file and created the appropriate constructor as follows:
public partial class MyEntities : DbContext
{
public MyEntities(System.Data.Common.DbConnection conn, bool contextOwnsConnection = false)
: base(existingConnection: conn, contextOwnsConnection: contextOwnsConnection)
{
}
}
However, when I run the code the moment it hits a call to the new DbContext constructor, I get the following UnintentionalCodeFirstException() error thrown by OnModelCreating in my EDMX file:
"Code generated using the T4 templates for Database First and Model First development may not work correctly if used in Code First mode. To continue using Database First or Model First ensure that the Entity Framework connection string is specified in the config file of executing application. To use these classes, that were generated from Database First or Model First, with Code First add any additional configuration using attributes or the DbModelBuilder API and then remove the code that throws this exception."
Am I missing something obvious here, or can it just not be done with Database-First?
Clearly, I could just use two connections, one for my SqlConnection object, and another for my DbContext object but if I can, naturally I'd prefer to use a single connection if possible.
Any and all help greatly appreciated. For full disclosure, I'm using SQL-Server 2012, .NET 4.5.1, C# and EF6.0.2.

Connection strings used by the designer are not regular connection strings. Rather they are EntityConnection strings. The difference is that entity connection strings contain additional information about where to find metadata describing the model which is in form of the edmx at design time - read more here. Code First uses just regular connection strings since it builds the model on the fly based on the code. So, the UnintentionalCodeFirstException is preventing the user from using CodeFirst functionality with edmx models because the model is specified in the edmx and not in the code and if it was allowed you would effectively end up using two different models (one from edmx and one built from the code) which very likely won't be in sync which would result in weird behavior or even could lead to data corruption and crashes/exceptions.
Since the EntityConnection is derived from DbConnection and just wraps regular connection you can use it in places where you would use the provider connection. Alternatively you can access the wrapped provider connection using the StoreConnection provider on the EntityConnection.

I know this is an old thread, but later versions of Entity Framework actually can handle a shared connection, so I offer this as an alternative answer.
You can initialize an instance of the entity container with a shared connection. Use the EntityConnection(MetadataWorkspace workspace, DbConnection connection, bool entityConnectionOwnsStoreConnection) overload and specify false for the entityConnectionOwnsStoreConnection parameter. Then pass it into your context constructor as the existing connection. The EntityConnection will then prevent the connection from being automatically closed and disposed with the context.
Example:
using (var conn = new SqlConnection("..."))
{
conn.Open();
// Execute some ADO queries.
var md = new MetadataWorkspace(new[]{"res://*/SampleModel.csdl","res://*/SampleModel.ssdl","res://*/SampleModel.msl"}, new[]{System.Reflection.Assembly.GetExecutingAssembly()});
// Create the EntityConnection so the existing connection is not disposed.
var ec = new EntityConnection(md, conn, false);
using (var context = new SampleContext(conn, contextOwnsConnection: false))
{
// Do something using the entity context.
}
// Entity context is disposed but connection remains open.
// Do more ADO stuff.
}
Yes, it was a pain to figure out this stuff by examining System.Data and Entity Framework source code.
This pattern may be used within a TransactionScope to prevent escalation to a distributed transaction by virtue of using the same database connection.

Related

DbMigrator does not detect pending migrations after switching database

EntityFramework migrations become useless after switching to new Context.
DbMigrator is using list of Pending Migrations from first database instance, which makes means no migrations are applied to other databases, which then leads to errors during Seed();
C# .NET 4.5 MVC project with EF 6
MS SQL Server 2014, multiple instances of same database model.
CodeFirst approach with migrations.
DbContext initializer is set to null.
On Application Start we have custom Db Initialization to create and update databases. CreateDatabaseIfNotExists is working as intended, new databases have all migrations applied. However both MigrateDatabaseToLatestVersion initializer and our custom one are failing to update databases other than first one on list.
foreach (var connectionString in connectionStrings)
{
using (var context = new ApplicationDbContext(connectionString))
{
//Create database
var created = context.Database.CreateIfNotExists();
var conf = new Workshop.Migrations.Configuration();
var migrator = new DbMigrator(conf);
migrator.Update();
//initial values
conf.RunSeed(context);
}
}
context.Database.CreateIfNotExists(); works correctly.
migrator.GetLocalMigrations() is always returning correct values.
migrator.GetPendingMigrations() after first database is returning
empty list.
migrator.GetDatabaseMigrations() is mirror of pending migrations,
after first database it contains full list event for empty databases.
Fetching data (context.xxx.ToList()) from Db instance confirms connection is up and working, and links to correct instance.
Forcing update to most recent migration with migrator.Update("migration_name"); changes nothing. From what I gather by reading EF source code, it checks pending migration list on its own, which gives it faulty results.
There seems to be some caching going in under the hood, but it eludes me how to reset it.
Is there a way to perform migrations on multiple databases or is it yet another "bug by design" in EF?
Edit:
Real problem is DbMigrator creating new Context for its own use. It does it via default parameterless constructor, which in my case had fallback to default (first) connection string in web.Config.
I do not see good solution for this problem but primitive workaround in my case is to temporarily edit default connection string:
var originalConStr = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"].ConnectionString;
var setting = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"];
var fi = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
//disable readonly flag on field
fi.SetValue(setting, false);
setting.ConnectionString = temporaryConnectionString; //now it works
//DO STUFF
setting.ConnectionString = originalConStr; //revert changes
Cheat from: How do I set a connection string config programatically in .net?
I still hope someone will find real solution so for now I will refrain with self-answer.
You need to correctly set DbMigrationsConfiguration.TargetDatabase property, otherwise the migrator will use the default connection info.
So in theory you can do something like this
conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(...);
Unfortunately the only 2 public constructors of the DbConnectionInfo are
public DbConnectionInfo(string connectionName)
connectionName: The name of the connection string in the application configuration.
and
public DbConnectionInfo(string connectionString, string providerInvariantName)
connectionString: The connection string to use for the connection.
providerInvariantName: The name of the provider to use for the connection. Use 'System.Data.SqlClient' for SQL Server.
I see you have the connection string, but have no idea how you can get the providerInvariantName.
UPDATE: I didn't find a good "official" way of taking the needed information, so I've ended using a hack with accessing internal members via reflection, but still IMO it's a quite more safer than what you have used:
var internalContext = context.GetType().GetProperty("InternalContext", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(context);
var providerName = (string)internalContext.GetType().GetProperty("ProviderName").GetValue(internalContext);
var conf = new Workshop.Migrations.Configuration();
conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, providerName);

Drop a database using entity framework

I'm trying to remove a database form my application using entity framework.
The code I use is the following:
using (var dbContext = container.Resolve<ApplicationDbContext>())
{
dbContext.Database.Delete();
}
According to msdn this should work but nothing happens.
the dbContext is registered using ContainerControlledLifetimeManager and should be the same instance used to create the DB.
Adding, updating and deleting instances of entity types needs dbContext.SaveChanges() to reflect changes.
However dbContext.Database.Delete() does not need dbContext.SaveChanges().
If you open connection for example from Sql Management Studio to your database and try to dbContext.Database.Delete() then you receive Cannot drop database "dbContext" because it is currently in use. If you restart you sql server, you drop those connections and then retry dbContext.Database.Delete() you successfully drop database.
Last thing is refresh database list in Sql Management Studio in order to see that database is not there any more.
Testing with this code snippet:
using (var dbContext = new dbContext())
{
dbContext.Database.Delete();
}
After #moguzalp and this (MSDN Database.Delete Function), I came with a solution for my case:
using System.Data.Entity;
Database.Delete("connectionStringOrName");
In my case I was trying to Recreate a mssqllocalDb database for test purposes. But whenever I used the same DbContext (or an immediately new one, disposing the first and opening another), it looked like the database was still up when I tried to create it.
Bad Example: (x)
public static void RecreateDatabase(string schemaScript)
{
using (DbContext context = new DbContext("connectionStringName"))
{
context.Database.Delete(); // Delete returns true, but...
Database database = context.Database;
database.Create(); // Database already exists!
database.ExecuteSqlCommand(schemaScript);
}
}
Working example: (/)
public static void RecreateDatabase(string schemaScript)
{
Database.Delete(); // Opens and disposes its own connection
using (DbContext context = new DbContext("connectionStringName")) // New connection
{
Database database = context.Database;
database.Create(); // Works!
database.ExecuteSqlCommand(schemaScript);
}
}
Context: I'm using this on an [AssemblyInitialize] function to test my ModelContexts
All of your changes occurred on local variable dbcontext.
That is final kick > add dbContext.SaveChanges(); at the end of your using block like so:
using (var dbContext = container.Resolve<ApplicationDbContext>())
{
dbContext.Database.Delete();
dbContext.SaveChanges();
}

Unable to use existing database in unit tests with Effort framework

I am trying to write test using a database, hosted in Azure SQL, with Effort framework on Entity Framework 6.
When executing the following code, an exception is thrown:
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
EffortProviderConfiguration.RegisterProvider();
}
[TestMethod]
public void TestMethod1()
{
const string connectionString = "Data Source=***;Initial Catalog=my_catalog;User ID=user;Password=password;provider=System.Data.SqlClient";
IDataLoader loader = new EntityDataLoader(connectionString);
using (var ctx = new UsersDbContext(Effort.DbConnectionFactory.CreatePersistent("cool", loader)))
{
var usersCount = ctx.Users.Count();
}
}
Exception thrown in Count() execution:
Effort.Exceptions.EffortException: Unhandled exception while trying to initialize the content of 'Table' table ---> System.ArgumentException: Keyword not supported: 'data source'.
The same exception is thrown when replacing EffortProviderConfiguration.RegisterProvider() with app.config settings.
When using exactly the same connection string for creation of the UsersDbContext it succeeds and the data is accessible. In addition, creating context with Effort persistent or transient mode, without connection string, works well too.
What should be done to initialize a connection with existing data from a real DB?
If like me you're confused as to why you have to give Effort a connection string at all (since it works off an in-memory database, and you provide your context a connection directly), the documentation makes it a bit clearer - it's only required if you're using database-first or model-first variants of the Entity Framework, because the Entity connection string provides the information necessary for Effort to locate your model so that it can build a schema from it!! So you can safely fill the server/database/user id/password portions of the connection string with dummy names.
This also makes it clear that the custom default DbConnectionFactory approach only works for code-first, which explains the first few hours of errors I was getting... For model first or database first, you have to inject an Entity Connection into your entity class, as described here.
A useful tip - because your generated entity model class is a partial class, you can create another code file in the same assembly, give it the same namespace and make it also a partial class, and you can add the second constructor necessary for setting the EntityConnection to that code file instead, that way when you modify/recreate your entity model, the code with the custom constructor won't get deleted by the t4 template.
You are specifying connection string in wrong format. You are using ADO.NET/Linq2SQL connection string format, whenever
EntityDataLoader requires connection string that will fit EntityConnection (class from EntityFramework).
You can read about connection strings for EF here: http://msdn.microsoft.com/en-us/library/system.data.entityclient.entityconnection.connectionstring(v=vs.110).aspx
Saying it short, your connection string should look like:
"Provider=System.Data.SqlClient;
Metadata=c:\metadata|c:\Metadata\Sql;
Provider Connection String='Data Source=localhost; Initial Catalog=AdventureWorks;Integrated Security=True;Connection Timeout=60' "
Right now in your code you are specifying only Provider Connection String part.
I believe your connection string must be provided as EF style connection string. Such as in your app.config or in your webconfig:
<add name="dbConnectionString" connectionString="metadata=res://*/Models.YourEntityModel.csdl|res://*/Models.YourEntityModel.ssdl|res://*/Models.YourEntityModel.msl;provider=System.Data.SqlClient;provider connection string="data source=yourdatasource;initial catalog=yourdb;persist security info=True;user id=username;password=password;multipleactiveresultsets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
[TestMethod]
public void MyTestMethod()
{
//Arrange
//Then you can specify your connection string with its name:
EntityConnection connection =
Effort.EntityConnectionFactory.CreateTransient("name=dbConnectionString");
//Act
var usersCount;
using (MyDbContext ctx= new MyDbContext(connection))
{
usersCount = ctx.Users.Count();
}
//Assert
//Put your assert logic here:
Assert.IsTrue(usersCount == 100);
}

EntityFramework CodeFirst and EDMX together in TransactionScope

I need to combine in one application
code generated from DB to EDMX file before compilation and
code generated and compiled during runtime by application itself, where generated code uses CodeFirst to access DB.
Remark: Codes in 1. and 2. have different DbContexts, access same database, but different tables.
It looks, that when I am using instances of type 1. and 2. in different transaction scopes, everything works fine. But when I try to use them together in one transaction scope, I get error (in case when EDMX is called first)
System.Reflection.TargetInvocationException: Exception has been thrown by the ta
rget of an invocation. ---> System.Data.ProviderIncompatibleException: An error
occurred while getting provider information from the database. This can be cause
d by Entity Framework using an incorrect connection string. Check the inner exce
ptions for details and ensure that the connection string is correct. ---> System
.Data.ProviderIncompatibleException: **The provider did not return a ProviderManif
estToken string.** ---> System.Transactions.TransactionException: **The operation is
not valid for the state of the transaction.**
and error (in case when CodeFirst is used first)
System.Data.MetadataException: Schema specified is not valid. Errors:
(0,0) : error 0175: **The specified store provider cannot be found in the configur
ation, or is not valid.**
at System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors()
To complicate my situation even more I have to add this: described behaviour I have only in case when DB is on remote server. If I am working with local DB, everything looks OK. My suspicion is that Distributed Transaction Coordinator can play its role...
Main question: is it possible to combine EDMX and CodeFirst in one TransactionScope. If yes then how?
Any help would be appreciated. Milos
CodeFirst creates an EDMX based on your classes and it's not possible to load an exisiting edmx file. However you can generate classes from your database (e.g. using EF Power Tools) and configure your model so that the EDMX generated by your CodeFirst app is the same as the one you would like to load. You can use TransactionScope with Entity Framework. The error messages you are getting are not related to transaction scope but to a missing or incorrectly used provider.
I used code first to save to two different databases came up with errors connection string was wrong so i did this and it worked....
try
{
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
{
MegaBotExtractorDBContext2 db = new MegaBotExtractorDBContext2();
MegaBotExtractorDBContext db1 = new MegaBotExtractorDBContext();
FullUri newUri = new FullUri();
HostUri NewHostUri = new HostUri { HostUriName = "google10.com" };
db1.HostUris.Add(NewHostUri);
db1.SaveChanges();
using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
db.FullUris.Add(newUri);
db.SaveChanges();
ts2.Complete();
ts.Complete();
}
}
}
catch { }

linq to entities changing database connection string in code

I am using the devart component dotconnect for postgresql. I have created the site using linq to entities, however, I would like each user to have a seperate database. This means that I need to change the connection string for each person that has logged in. I understand the main part of how to generate a new connection string etc, however, when i pass that as a paramater to the object context object it comes back with the error
"user id keyword not supported, "
if i create a class that generates an entity connection the error message changes to:
"Unable to load the specified metadata resource."
Cannot work out what I have done wrong in these instances.
ok so, as usual, when i posted this question, about 3 minutes later i found the problem. The entity connection string, for general purposes should have a cool little
res://*/
this makes the metadata work. This solves the problem of metadata resource and this works. So to help others who may, like me, have spent development time doing this, i created a class, with a method like so.
public static string getConnString(string database)
{
string connectionstring = "User Id=USER ID HERE;Password=PASSWORD HERE;Host=server;Database="+database+";Persist Security Info=True;Schema=public";
EntityConnectionStringBuilder newconnstring = new EntityConnectionStringBuilder();
newconnstring.Metadata = #"res://*/";
newconnstring.Provider = "Devart.Data.PostgreSql";
newconnstring.ProviderConnectionString = connectionstring;
return newconnstring.ToString();
}
then create a constructor like so
dataEntities data = new dataEntities(databaseConnection.getConnString(INSERTDBNAMEHERE);
Then we can reference that in the same way as a usual linq statement. Simples!!

Categories

Resources