问题
MVC Project using EF 5 code first and .NET 4.5.
I was looking for a way to wrap dbContext and SimpleMembershipProvider on a single transaction. I tried to use TransactionScope but since the membership provider will open another connection I get an exception (MSDTC on server 'servername' is unavailable).
So I though I could use ObjectContext.Connection.BeginTransaction instead. The membership provider won't be part of the transaction but the idea is to have it somewhere where if it fails the transaction won't be committed.
Book bk1 = default(Book);
Book bk2 = default(Book);
object obc = (IObjectContextAdapter)dbContext;
obc.ObjectContext.Connection.Open();
using (tx == obc.ObjectContext.Connection.BeginTransaction) {
bk1 = new Book {
Name = "Book 1 Name",
Location = "USA"
};
dbContext.Books.Add(bk1);
dbContext.SaveChanges();
bk2 = new Book {
Name = "Book 2 Name. Book one Id is: " + bk1.Id,
Location = "USA"
};
dbContext.Books.Add(bk2);
dbContext.SaveChanges();
// this is not part of the transaction,
// however if it trhows an exception the transaction is aborted.
// I'm assuming that if we got here, the commit won't fail...
// But is it really true?
WebSecurity.CreateUserAndAccount("username", "123456");
tx.Commit();
}
Anyways, based on the above code:
- If WebSecurity.CreateUserAndAccount fails, the whole thing fails, which is expected.
- If any of the SaveChanges method fails, once again the whole thing fails because we dont get to the point where CreateUserAndAccount is executed.
This whole thing bring me to a question: Is it safe? What I mean is:
Is there any possibility the "Commit" method will throw an exception(of fail somehow) after we successfully execute DbContext.SaveChanges ? If it happens we will endup with an orphan user because the provider is not part of the transaction.
I appreciate any comments or advice on this.
Quick note: There is this nice article explaining why I have to cast dbContext to IObjectContextadapter instead of using its own connection property but I can't find it anymore.
回答1:
Yes, Commit
can certainly throw. One trivial example is if the connection drops. There are other cases, of course.
You have a few choices:
- Transactions on the same DB won't escalate to distributed if the connection is shared. So you could use one connection for both EF and your
WebSecurity
connection. - Start the distributed transaction controller on the server and live with the escalation. Not the worst thing in the world.
- Instead of using a transaction, change the order of operations such that a partially successful operation can be completed or undone later. E.g., detect and clean up "orphan" users.
来源:https://stackoverflow.com/questions/12321659/wrapping-membership-provider-and-dbcontext-on-single-transaction