EF Update Formviews in a Transaction - c#

I want to update two formviews within a transaction. if one of them fails the other should fail too. Formviews have their own entity datasources.
button1_click(..........)
{
formview1.updateItem(true);
formview2.updateItem(true);
}

Ok so this may not be the simplest thing in the world.
The basic answer is that yes you can do it. And the code would be similar to this if the updateItem method opens the db connection.
using (TransactionScope scope = new TransactionScope())
{
formview1.updateItem(true);
formview2.updateItem(true);
scope.Complete();
}
If on the other hand the connections are already open by the time updateItem is called then you will need to do something more like
using (TransactionScope scope = new TransactionScope())
{
formview1.Connection.EnlistTransaction(Transcation.Current);
formview2.Connection.EnlistTransaction(Transcation.Current);
formview1.updateItem(true);
formview2.updateItem(true);
scope.Complete();
}

Related

EF6 transaction error in ObjectContext

please can someone help i'm trying to use Transaction in EF6. it's my first try in transaction in .net.
i have read the manual by Microsoft in their site.
http://msdn.microsoft.com/en-us/data/dn456843.aspx and i would to use a transaction for user registration but nothing is working. Im using EF objectContext.
here is a little piece of my code :
using (var context= new MyModelContainer())
{
using(var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
// code here to create user
context.savechanges();
// code here to add role
context.savechanges();
dbContextTransaction.Commit();
}
catch
{
dbContextTransaction.Rollback();
}
}
}
My problem is that Visual Studio does not even recognize Database.BeginTransaction(). may be because we are using objectContext? i never use transaction. i change to use another database where our model is DbContext it's seems to work.
How can we use objectContext with transaction also (mean not distributed systems) ?
any tutorial please ?
I tried transaction scope but it's seem to work but i read that it's for distributed system ((( it's means that performance going down. any suggestion ?
thanks for your time !!!
Replace the line
using(var dbContextTransaction = context.Database.BeginTransaction())
with this:
using(var dbContextTransaction = new TransactionScope())
and remove the call to Rollback. TransactionScore will do what you need.
TransactionScope is compatible with distributed systems, but if you do not make a call to other db's and use it as described, then it is more than good enough for your needs. If you try to make a call to save to another db (for example) then it will require special privileges and an exception will be thrown.

TransactionScope, EF DbContext, and Dirty Read

I've been Googling for an answer for the past few hours, but haven't found an answer, so I'm hoping someone here can point me in the right direction.
I am wondering how to do a dirty read with EF DbContext (Code-First) within a TransactionScope. For example
DbContext context = new MyEntities();
using(TransactionScope scope = new TransactionScope())
{
context.SomeTable.Add(someObject);
context.SaveChanges();
var objects = context.SomeTable.Where(...).Select(...); //times out here on the read, because the write above locks the tables
//modify each object
context.SaveChanges();
scope.Complete(); //transaction is a success only if the entire batch succeeds
}
I have tried wrapping the read call with the following:
using(TransactionScope scope2 = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions{IsolationLEvel = IsolationLevel.ReadUncommitted}))
{
var objects = context.SomeTable.Where(...).Select(...); //times out here on the
}
What is the proper approach?
It has finally clicked for me what the problem is: EF is not integrating with TransactionScope the way L2S nicely does. This means that EF opens and closes the connection for each operation requiring the server (saving changes or querying).
This gets you distributed transactions and a distributed deadlock.
To solve this, manually open and close the EF StoreConnection to ensure that there is exactly one connection for the duration of the transaction.
You can set the IsolationLevel for TransactionScope like this...
var transactionOptions = new System.Transactions.TransactionOptions();
transactionOptions.Timeout = new TimeSpan(0, 0, 30);
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
using (var transactionScope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
{
...
}
You can just execute alter database your_db_name set READ_COMMITTED_SNAPSHOT on;(available from SQLServer 2005) on the server, and readers won't be blocked by writters (assuming reading transaction in read committed isolation level).

.net transaction scope block second transaction

In my application I have the following pattern:
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required))
{
Function1();
Function2();
Function3();
}
My problen is that Function2 calls another function that connects to another DB ... and the transaction becomes distributed and I get an exception.
Is there any way in the code in which I can make a db call that is not part of the current transaction? My code in Function2 does just a read ... so I don't want to be part of current transaction.
Thanks, Radu
Around function2 you could create a new transaction with TransactionScopeOption.RequiresNew, thus forcing it into its own separate transaction. Since only one resource (the other database) will be used in that transaction is shouldn't become distributed.

TransactionScope problem with Message queues

I'm facing the next problem. I have a piece of code that goes like this:
DoSomething(){
using (TransactionScope scope = new TransactionScope())
{
InsertSomething();
InsertSomethingElse();
InsertYetAnotherThing();
ProcessDataRelatedWithThePreviousInserts();
scope.Complete()
}
}
In ProcessDataRelatedWithThePreviousInserts I check for a condition and if needed, the rest of the work flow is redirected to a Message Queue in other server. In the other server, I recover the message, and continue the workflow (basically, make some other insertions that are related with the ones on the DoSomething method).
This is in theory, because I only manage to do that if I remove the TransactionScope in the DoSomething method. Is there a way to accomplish what I want to do or I'll need to change the way the transactions are handled?
Did you already try
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
// ...
using (TransactionScope innerscope = new TransactionScope(TransactionScopeOption.Supress)
{
ProcessDataRelatedWithThePreviousInserts();
}
scope.Complete();
}
explicitly supressing the outer transaction for your call to ProcessDataRelatedWithThePreviousInserts().

Data committed even though System.Transactions.TransactionScope.Commit() not called

Under what circumstances can code wrapped in a System.Transactions.TransactionScope still commit, even though an exception was thrown and the outermost scope never had commit called?
There is a top-level method wrapped in using (var tx = new TransactionScope()), and that calls methods that also use TransactionScope in the same way.
I'm using typed datasets with associated tableadapters. Could it be that the commands in the adapter aren't enlisting for some reason? Do any of you know how one might check whether they are enlisting in the ambient TransactionScope or not?
The answer turned out to be because I was creating the TransactionScope object after the SqlConnection object.
I moved from this:
using (new ConnectionScope())
using (var transaction = new TransactionScope())
{
// Do something that modifies data
transaction.Complete();
}
to this:
using (var transaction = new TransactionScope())
using (new ConnectionScope())
{
// Do something that modifies data
transaction.Complete();
}
and now it works!
So the moral of the story is to create your TransactionScope first.
The obvious scenario would be where a new (RequiresNew) / null (Suppress) transaction is explicitly specified - but there is also a timeout/unbinding glitch that can cause connections to miss the transaction. See this earlier post (the fix is just a connection-string change), or full details.
Be aware how TransactionScope works:
It sets property System.Transactions.Transaction.Current at the begining of using scope and then set it back to previous value at end of using scope.
Previous value depends on where given scope is declared. It can be inside another scope.
You can modify code like this:
using (var sqlConnection = new ConnectionScope())
using (var transaction = new TransactionScope())
{
sqlConnection.EnlistTransaction(System.Transactions.Transaction.Current);
// Do something that modifies data
transaction.Complete();
}
I show this possibility for those who have their code more complicated and cannot simply change code to open DB connection first.
This example (C#, .NetFramework 4.7.1) shows how we can persist to the database even if the code is wrapped in a TransactionScope. The first insert will be rolled back, and the second insert will be inserted without transaction.
See my related post, where I ask for help in how to detect this.
using (var transactionScope = new TransactionScope())
{
using (var connection = new SqlConnection("Server=localhost;Database=TestDB;Trusted_Connection=True"))
{
connection.Open();
new SqlCommand($"INSERT INTO TestTable VALUES('This will be rolled back later')", connection).ExecuteNonQuery();
var someNestedTransaction = connection.BeginTransaction();
someNestedTransaction.Rollback();
new SqlCommand($"INSERT INTO TestTable VALUES('This is not in a transaction, and will be committed')", connection).ExecuteNonQuery();
}
throw new Exception("Exiting.");
transactionScope.Complete();
}

Categories