EntityFramework Transaction - Save to multiple tables - c#

Will the below code rollback the changes if there are any exception while saving?
using (SampleEntities context = new SampleEntities())
{
//Code Omitted
context.EmpPercAdjustments.AddRange(pp);
context.SampleJobs.AddRange(sampleJobs);
context.SaveChanges();
}
Or
Do I need to use transaction?
using (SampleEntities context = new SampleEntities())
{
//Code Omitted
using (System.Data.Entity.DbContextTransaction tranc = context.Database.BeginTransaction( ))
{
try
{
context.EmpPercAdjustments.AddRange(pp);
context.SampleJobs.AddRange(sampleJobs);
context.SaveChanges();
tranc.Commit();
}
catch (Exception ee)
{
tranc.Rollback();
}
}
}
Are there any advantages of using one over the others?

Yes it will rollback correctly.
In this case, you do not need to run an explicit transaction, because there is one already created by Entity Framework.
Creating a transaction by calling context.Database.BeginTransaction() is good, if you want f.e. to get the Id of just inserted record, something like this:
using (SampleEntities context = new SampleEntities())
{
using (System.Data.Entity.DbContextTransaction trans = context.Database.BeginTransaction( ))
{
context.SampleJobs.Add(newJob);
context.SaveChanges();
var jobId = newJob.Id;
//do other things, then commit or rollback
trans.Commit();
}
}
In this case, after calling SaveChanges(), the changes made on context objects are applied (so you can read database generated Id of added object in your scope), but they still have to be commited or rolled back, because changes are only dirty written.
Defining an explicit transaction can also be useful, if you have multiple methods that can modify context objects, but you want to have a final say, if changes they made will be all commited or not.

Related

Two nested Entity Framework contexts, sharing a transaction

I have code that looks like the example below. There's an explicit transaction involved because of some database tomfoolery that needs to be done via a SP, and a save changes in the middle of it all. (Exception handling, rollbacks, etc.. omitted):
void OuterMethod(MyDatbase context)
{
using(var dbTrans = context.Database.BeginTransaction())
{
// some stuff, the save puts the data where the SP can see it
Stuff(context);
context.SaveChanges();
// now some SP stuff
context.Database.ExecuteSqlCommand(#"spFoo", params);
// more stuff
MoreStuff(context);
AlmostUnrelatedCode(context);
context.SaveChanges();
dbTrans.Commit();
}
}
Right now the method AlmostUnrelatedCode() -- which is only marginally related to the process above -- needs a nice, fast, disposable read-only context 99% of the time. I have a factory that will serve me up the right kind of context when I need it. The 1% of the time it's called from the middle of that block above.
MyDatabase localReadOnlyContext;
void AlmostUnrelatedCode(MyDatabase context)
{
if ( context.Database.CurrentTransaction != null )
{
// Must use the context passed or everything deadlocks :(
localReadOnlyContext = context;
disposeContextLater = false;
}
else
{
// I just want to do this all the time
localReadOnlyContext = _contextFactory.CreateReadOptimized();
disposeContextLater = true;
}
// Do many, many things with my read-optimized context...
// The Dispose() on the class will check for disposeContextLater
}
What I'd like to do is to get rid of that transaction check, and in fact not need to pass the outer context at all if I can help it.
What I've tried:
Just ignoring what's going on in the outer transaction and using the context I generate all the time. Problem: deadlocks.
Trying to get the outermost transaction into the EF context I create with the _contextFactory. Problem: EF context constructors don't allow you to pass an existing transaction; also Database.CurrentTransaction has no setter.
Pulling the whole transaction out into a TransactionScope that wraps everything up. Problem: the method OuterMethod passes in the context, and I don't have control of the caller.
What I can't try:
Dirty reads/nolock. AlmostUnrelatedCode() needs the data as written so far.
I'd rather not:
Just keep using the outer context while inside of AlmostUnrelatedCode. AlmostUnrelatedCode deals with a lot of data trees and that context gets fat and unhappy really fast. It pollutes its context with crap really fast, and I'd rather just dispose of it when I'm done.
you can prevent the deadlocks by using one connection for multiple contexts.
example
var efConnectionString = ConfigurationManager.ConnectionStrings["SomeEntities"].ConnectionString;
// note EntityConnection, not SqlConnection
using (var conn = new EntityConnection(efConnectionString)) {
// important to prevent escalation
await conn.OpenAsync();
using (var c1 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count1 = await c1.SomeEntity1.CountAsync();
}
using (var c2 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count2 = await c2.SomeEntity21.CountAsync();
}
}
in your case just get the connection from the context and reuse it
context.Database.Connection
Can't you separate things done in AlmostUnrelatedCode like this:
void AlmostUnrelatedCode()
{
var context = _contextFactory.CreateReadOptimized();
AlmostUnrelatedCode(context);
context.Dispose();
}
void AlmostUnrelatedCode(MyDatabase context)
{
// Do many, many things with context...
}
Now you can call AlmostUnrelatedCode(with param) from your OuterMethod. And maybe there is even more to be separated. Consider SOLID.

How entity framework SaveChanges works?

I try to understand how EF creates DB requests from object manipulations in code. My test scenario is simple:
using(var context = new Context())
{
var entity = context.Entities.First();
entity.A = "TST";
entity.B = "WrongValue";
context.SaveChanges();
}
My idea is to test, how EF deal with transaction.
Change A with correct value and change B with wrong value (non existing FK)
I track what happening in SQL Server DB.
I execute code and nothing change in DB, that was expected.
Strange part is that there are two independant SQL request and I don't understand how EF revert first one.
Both Entity Framework and EntityFramework core code are open source. You can check the code at
Entity Framework - Link
Entity Framework Core - Link
If you see the internal code of Save method (pasted the code snapshot below) then you can validate that it internally it creates a transaction if an external transaction is not provided.
internal int SaveChangesInternal(SaveOptions options, bool executeInExistingTransaction)
{
AsyncMonitor.EnsureNotEntered();
PrepareToSaveChanges(options);
var entriesAffected = 0;
// if there are no changes to save, perform fast exit to avoid interacting with or starting of new transactions
if (ObjectStateManager.HasChanges())
{
if (executeInExistingTransaction)
{
entriesAffected = SaveChangesToStore(options, null, startLocalTransaction: false);
}
else
{
var executionStrategy = DbProviderServices.GetExecutionStrategy(Connection, MetadataWorkspace);
entriesAffected = executionStrategy.Execute(
() => SaveChangesToStore(options, executionStrategy, startLocalTransaction: true));
}
}
ObjectStateManager.AssertAllForeignKeyIndexEntriesAreValid();
return entriesAffected;
}
So your below code will internally wrapped inside a transaction which you can validate in SQL Profiler..
using(var context = new Context())
{
var entity = context.Entities.First();
entity.A = "TST";
entity.B = "WrongValue";
context.SaveChanges();
}
However, SQL profiler does not start logging transaction so you need to configure that in trace setting. See the below screenshot of SQL profiler new Trace setting, here, I have checked Show All events. After that Transaction category is being displayed. You can subscribe for Begin Tran, Commit Tran and Rollback Tran events to validate transaction statements. When you will run your scenario, you can see that Begin and Rollback should be logged.

How to insert data in transaction using entity-framewrok-4?

How to insert data in trasaction using entity-framewrok-4?
I have a order object and it has other collection that i need to inset in database in transaction.
cOrder->
->lstOrderItems
-->lstDressing, lstTopping, lstspecialInstruction
->userDetails
->trasactionTblsdetails
Also opinion required:-
1) Is it good to maintain transaction at database level or entity framework level.
2) What should be coding style.
right now i am following it this way but as i can predict it would require a lot of code but want to know this is only the way it work or far better solution exist.
public static void SaveOrder()
{
using (EposWebOrderEntities Ctx = new EposWebOrderEntities())
{
using (TransactionScope scope = new TransactionScope())
{
// do something...
Ctx.order.SaveChanges();
// do something...
//foreach to save the order items to databaswe
Ctx.orderitems.SaveChanges();
// do something...
//foreach to save in dressing tbl
Ctx.dressing.SaveChanges();
//foreach to save in topping tbl
Ctx.topping.SaveChanges();
//foreach to save in dressing tbl
Ctx.dressing.SaveChanges();
//foreach to save in spinst tbl
Ctx.dressing.SaveChanges();
scope.Complete();
success = true;
}
}
}
Use it this way. Is sufficient in most scenarios.
public static void SaveOrder()
{
using (EposWebOrderEntities Ctx = new EposWebOrderEntities())
{
Ctx.Entry<Order>(order).State = EntityState.Added;
Ctx.Entry<Dressing>(dressing).State = EntityState.Added;
Ctx.SaveChanges();
}
}
http://msdn.microsoft.com/en-us/library/dn456843.aspx
In all versions of Entity Framework, whenever you execute SaveChanges() to insert, update or delete >on the database the framework will wrap that operation in a transaction. This transaction lasts >only long enough to execute the operation and then completes. When you execute another such >operation a new transaction is started.

Transactions commit and rollback with Entity Framework, Mysql and LINQ to SQL in C#

My problem is that the transaction is not working properly it should not save the data for one table if an exception occurs during the trascation
When all the table is correct then only save data.
Consider the following:
databaseEntites objEntites = null;
using (objEntites = new databaseEntites())
{
objEntites.Connection.Open();
using (System.Data.Common.DbTransaction transaction =
objEntites.Connection.BeginTransaction())
{
try
{
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
transaction.Rollback();
}
}
}
In this my second table column name is not correct and on SaveChanges() giving the exception.
When i see the database and found that it saving the data for customer table which is wrong i want data will go in the customer table when all table is correct and this savechanges either save for all table or not save for any.
For this i have also try the TransactionScope
using (TransactionScope tscope =
new TransactionScope(TransactionScopeOption.RequiresNew))
{
......all the code here....
objEntites.SaveChanges(false);
tscope.Complete();
objEntites.AcceptAllChanges();
}
But its giving the same issue as described above.
Thanks in advance.
You can use database transaction or EF TransactionScope. For using database transaction it is enough to do as below:
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
//Some stuff
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
And for using second way that EF TransactionScope just use easily as below:
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
//Some stuff
scope.Complete();
}
catch (Exception)
{
//Don't need call any rollback like method
}
}
The point is no Rollback() method exist in the TransactionScope (against the normal ADO.NET Transaction) and Unless you call the Complete() method, the transaction do not complete and all the changes are rolled back automatically. You can see MSDN for better understand: http://msdn.microsoft.com/en-us/data/dn456843.aspx
Hope this help
If you have already a try ... catch block, you don't need using - just add finally. The following example should have everything you need:
databaseEntites objEntites = null;
var System.Data.Common.DbTransaction transaction = null;
try
{
objEntites = new databaseEntites();
objEntites.Connection.Open();
transaction = objEntites.Connection.BeginTransaction();
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
if (transaction != null) transaction.Rollback();
}
finally
{
if (objEntites != null && objEntites.Connection.State != System.Data.ConnectionState.Closed
&& objEntites.Connection.State != System.Data.ConnectionState.Broken)
objEntites.Connection.Close();
}
Hints:
The finally block is executed even after an exception has occured, hence in case of an exception the exception handling code is executed first, then the code in the finally block. Only if severe exceptions (system errors) - such as a StackOverflowException - occur, it is not executed but you can't handle such kinds of exceptions anyway easily. For more information about this topic please look here.
For SaveChanges you can also use the option System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave, which I prefer because it guarantees that every entity object has its changes accepted after successful save.
BeginTransaction allows also to specify the kind of transaction, but I would not use the parameter options because if you omit it then it creates a transaction as specified by the database's default - so the administrator is still able to change this easily if required. But if you need to specify it, ensure that you don't hardcode it, but allow to configure it in the App.Config or Web.Config file.
Please let me know if that works for you.

SaveChanges() for multiple dbcontext with TransactionScope in Entity Framework 4.1

I am using SaveChanges() method as given below:
objAdbContext of Database A
objBdbContext of Database B
Updating table of DB A as given below
public string SaveA()
{
//Some stuff
objAdbContext.SaveChanges();
string result=UpdateDatabaseB(some parameters)
//Some stuff
}
public string UpdateDatabaseB(some parameters)
{
//Some stuff
objBdbContext.SaveChanges();
return "Success";
}
This case Database B is not getting updated. Is it correct way of updating multiple databases?
Both are independent Databases and How to implement TransactionScope in this case?
Try this:
using (TransactionScope scope = new TransactionScope())
{
// Save changes but maintain context1 current state.
context1.SaveChanges(SaveOptions.DetectChangesBeforeSave);
// Save changes but maintain context2 current state.
context2.SaveChanges(SaveOptions.DetectChangesBeforeSave);
// Commit succeeded since we got here, then completes the transaction.
scope.Complete();
// Now it is safe to update context state.
context1.AcceptAllChanges();
context2.AcceptAllChanges();
}
This sample was taken from this blog post:
Managing Transactions with Entity Framework 4

Categories

Resources