Following the advice in another SO question I'm trying to optimize a save (insert only) operation that handles several thousand records by splitting the save operation into batches of X records. Naturally, I want these operations to be part of a single transaction.
We are using an EF6 Database First model.
MSDN has an article about transactions in EF6 and onwards that describes opening a connection and starting the transaction yourself, passing the connection to the DbContext
constructor and calling UseTransaction
afterwards.
However, this doesn't seem to work with Database First - it keeps invoking DbContext.OnModelCreating
which in turn throws an UnintentionalCodeFirstException
.
Even without transactions it doesn't work. Example:
using (var conn = new SqlConnection("..."))
using (var context = new BloggingContext(conn, contextOwnsConnection: false))
{
context.Blogs.Add(blog); // invokes ModelCreating and fails.
context.SaveChanges(); // never gets this far
}
The exception actually suggests providing an EntityConnection
instead, so I tried the following:
using (var sourceContext = new BloggingContext())
{
using (var anotherContext = new BloggingContext(
sourceContext.Database.Connection, // supply existing EntityConnection
contextOwnsConnection: false))
{
context.Blogs.Add(blog); // still fails.
context.SaveChanges(); // never gets this far
}
}
Using TransactionScope
is another option, but unfortunately this will inevitably escalate to MSDTC since multiple connections are opened, and this is not an option.
Here's a simplified version of my attempt at optimizing the operation. How do I get the contexts that are newed up for each batch to be included in a single transaction using Database First?
foreach (List<Blog> blogBatch in blogBatches)
{
using (var context = new BloggingContext())
{
context.Configuration.AutoDetectChangesEnabled = false;
context.Blogs.AddRange(blogBatch);
context.SaveChanges();
}
}
EDIT: Found this question that seems to deal with the same issue, although still without a useful answer.