SQL Server SaveChanges() in an explicit vs implicit transaction in EF Core

u7up0aaq  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(135)

Intro

I'm developing an ASP.NET Core with SQL Server app that requires soft-delete on all records.

I'm using explicit BeginTransaction() in case an entity is soft-deleted so that all related entities may be deleted as well.

Context

The reason I'm not doing it implicitly with just SaveChanges() is when I use FromSqlInteroplated to use some stored procedure to check if the entity has any related (not soft-deleted) entities; the results seems like they are from the actual database, but it does not work as I wanted, because I want to persist soft-delete changes before that stored procedure call is made.

Whereas when I use an explicit transaction with BeginTransaction() and SaveChanges after every delete operation, the stored procedure works as I want, even though at every SaveChanges , the changes are NOT persisted to the actual database, it is made against the context, how is this happening?

Question

Sorry if it seems like a stupid question, if calling SaveChanges will not persist changes against the actual db in an explicit uncommitted transaction context how does a stored procedure with FromSqlInteroplated work in that case, is the stored procedure query made against the actual database or the context?

kwvwclae

kwvwclae1#

With an explicit transaction, calls to SaveChangesare written to the database and as far as any queries you execute with that DbContext against the database, it will see them. What happens with queries outside of that transaction scope will depend on the isolation level of the database and the external query in question. For instance if your database is using a typical READ COMMITTED isolation level, while the transaction is open, external queries will be blocked until the transaction is committed or rolled back. If the database is using something like READ COMMITTED SNAPSHOT (often recommended for EF and other ORMs) then those third party queries will not be blocked, they will simply return data that excludes changes being made by the transaction.

For instance if I have a parent / child relationship and run the following:

using var context = new SoftDeleteDbContext();
using(var tx = context.Database.BeginTransaction());

var parent = context.Parents.Include(x=>x.Children).First(x => x.ParentId == 1);
var newChild = new Child("Dameon", 12, parent);
parent.Children.Add(newChild);
context.SaveChanges();

var data = context.Children.AsNoTracking().Where(x => x.ParentId == 1).ToList();

tx.Rollback(); // <-- Breakpoint Here

With a breakpoint on the Rollback, if I check the results of data I will see that my new child "Dameon" is returned. AsNoTracking() forces the query to the database. If you go to SSMS while this transaction is still running and run SELECT * FROM Children WHERE ParentId = 1 you would see all children except "Dameon" would be returned. He wouldn't appear in those results until the transaction is committed. After the rollback he won't appear in either queries. If you aren't using SNAPSHOT isolation then an SSMS query will be blocked by locks until the transaction is committed or rolled back.

If you want proof the row is in the database, you can use SELECT * FROM Children WHERE ParentId = 1 WITH (UNCOMMITTED) and "Dameon" appears.

相关问题