I am encountering this situation where EF6 is used to save an object to the database (into 2 separate tables). Thus, by executing SaveChanges() in EF, this generated 2 seperate exec sp_executesql insert... statements.
This is where the value generated from the first insert will be used as an input parameter to the second statement.
One thing I noticed though is that when the second statement failed in one case, no records were written.
I pulled up SQL-Profiler and did not see any rollbacks being made either; can't seem to figure out how this rollback actually occurred.
Appreciate any advice pls.
One thing I noticed though is that when the second statement failed in one case, no records were written.
I pulled up SQL-Profiler and did not see any rollbacks being made either; can't seem to figure out how this rollback actually occurred.
By calling SaveChanges() on your EF context, all modifications made to your entities (insert, update, delete etc) are wrapping into a single SQL transaction. This is a default behavior in EF.
The purpose of a transaction is to take all the SQL statements as a single operation, a unit of work. If one statement fails at the middle or at the end then all passed statements are rollback even those that are executed successfully.
To prevent this you can call SaveChanges() separately for each changes you made in your EF context.
Related
I have a client-side entity which contains a list of secondary entities, and this is represented on the DB as a foreign key constraint.
The client-side creation form allows to create both the main entity and its sub-entities at once, so on the DB side may have to save both a new main entity and some new sub-entities in one transaction. For this reason, I would like to use a stored procedure DB-side, and the most immediate approach would seem to pass the main entity's fields as arguments, with the sub-entities as a collection argument of some sort (likely a json string, to account for the heterogeneity of the sub-entities' data).
Within my stored procedure, first I would save the main entity, then I would save the sub-entities, possibly making use of a newly-generated main entity ID (as I understand, the procedure itself as a transaction, and a failure after writing the main entity would roll back said insertion/update).
My problem lies within saving the sub-entities (from here on just "entities"). While I understand that, for existing entities, I need to run each update on its own, I would like to use the multi-row insert syntax for new entity. For this reason, I want to build a prepared statement where I can grow the list of inserted tuples as the length of the entity list may vary, as in INSERT INTO ... VALUES <tuple1>, <tuple2>, ..., and here comes the pain...
It would of course be simplest to just concatenate raw values from each entity in order to build the query string, this is inviting SQL injection.
On the other hand, while I may build the insert statement using the $ notation, I would have a problem when executing it, wiring the parameters, that is:
-- prepare insert statement stmt
EXECUTE stmt USING <???> -- variable number of parameters to wire,
-- I don't know how to work around it.
Is there a way to use a single multi-row insert, or should I just fall back to inserting values one-by-one? I assume that a multi-row insert doesn't actually just repeatedly perform single-row inserts, and that there would be some benefit in using it over the one-by-one approach. Of course, I would rather avoid twisted workarounds that would slow the operation down beyond the benefit given by the multi-row insert.
You could package the tuples in a json[] and deparse them in the INSERT statement, but I don't really see the point.
I would just run single-row INSERT statements for each entity.
I guess you are worried about performance, but maybe all that parsing and deparsing would outweigh the advantage of inserting everything in a single statement.
Apart from that, I'd expect the code to cleaner and easier to maintain with simple INSERTs in a loop.
I am chasing an issue with MySql / EF Core where I randomly have an exception thrown saying Nested transactions are not supported. This exception does not occur when my system is only used by one user. But when I run my tests in parallel or when I have multiple users, the exception occurs. I looked at all the code and their is nothing that could create nested transactions.
The only piece of codes that scares me so far is something like the following:
using (var transaction = _context.Database.BeginTransaction())
{
// Create a few entities and add them to the EF context
...
// Insert the rows: hopefully at this point, my transaction is not commited yet.
_context.SaveChanges();
// I then want to update a few rows with a raw sql statement without
// having to load the entities in memory.
_context.Database.ExecuteSqlCommand("UPDATE ...");
// Now that I have inserted and inserted some data, I want to commit all these
// changes atomically.
transaction.Commit();
}
Is this safe? Am I guaranteed that my SaveChanges and ExecuteSqlCommand will be executed on the same MySqlConnection? I have the feeling that when I call SaveChanges, it closes my connection and puts it back on the connection pool. Then, my ExecuteSqlCommand takes a new connection from the pool (it may be the same one or another one). So my initial connection (the one where I opened the transaction) is put back in the pool and it could be reused by another thread.
This is just a hunch and I am totally not sure if this could cause the problem.
My real question in the end is:
is it safe to use SaveChanges and ExecuteSqlCommand within a transaction?
I upgraded from MySql.Data.EntityFrameworkCore/MySql.Data 6.10.1-beta to 6.10.3-rc and it looks like the problem is gone. Since the problem was random I can't be totally sure that the problem is indeed fixed, but so far so good.
EDIT:
3 years later, the problem was never observed anymore.
In our EF 5 application, when we get a SQL Server deadlock error on an insert or update, we immediately try the operation again. However, when we attempt to do so, we're getting the following error:
"Conflicting changes detected. This may happen when trying to insert multiple entities with the same key."
This error is not coming from SQL Server. This is an EF 5 error. And we are not attempting to insert multiple entities with the same key. IOW, we're not attempting to insert a duplicate row. However, I suspect this error means something else. But I'm not entirely certain I know what the issue is. If I had to guess, I would say that on the first attempt, EF sees where trying to insert an entity. It fails because of a deadlock. When we immediately try again, EF thinks we're trying to do the very same operation again, with the same key, and doesn't like it. Not sure how to get around this.
It sounds like you might be trying to execute your queries against the same instance of the DbContext. In which case, your changes are already pending from the last try.
Since there is no “undo pending changes” on the context, you must dispose and recreate the context in between “retries”.
We are contemplating the use of Entity Framework for a bulk upload job. Because we are interested in tracking the individual results of each recorded, I am trying to find out whether all items inserted are done as part of a transaction and if a single failure would cause a rollback, or if there is a way to track the result of each individual result.
I was contemplating just looping through the items and calling Save() on each and wrapping that in a try/catch block, but just calling save() on the context seems more effective if I can tap into the individual results.
Thanks!
By default each time you call SaveChanges all records that are pending are saved at once. All operations within a SaveChanges call are within a transaction (source) and so if a particular SaveChanges call it fails it will rollback the queries in that particular call, but obviously not previously successful calls.
Ultimately it depends on where you call SaveChanges as to how much of a rollback you'd get. You can extend the length of a transaction with TransactionScope see this article for some examples on how to extend the scope.
My own experience is that calling SaveChanges too often can really decrease performance, and slow the entire process down a lot. But obviously waiting too long can mean a greater memory footprint. I found that EF can easily handle several thousand rows without much of a memory problem.
Placing the code in a try...catch will assist you, but you may want to look at entity validation as you can place restrictions on the objects and then validation will occur on the objects before any SQL is generated.
I'm using Entity Framework 4.1 and have a seemingly simple requirement: I want to either get an entity by a unique key, or if it doesn't already exist, create it:
var user = db.Users.SingleOrDefault(u => u.Sid == sid);
if (user != null)
return user;
user = new User(sid);
db.Users.Add(user);
Normally this works fine, but when I run a bunch of tests together (using MSTest) one of them consistently fails with "Sequence contains more than one element". When I run that test by itself it works fine.
The problem seems obvious: multiple threads are calling the above code concurrently and each create a new User row. But what is the solution?
The proper solution is a transaction, of course, but I just cannot get it to work. EF won't use a normal DbTransaction if I start one. If I use a TransactionScope it either has no effect (the same error occurs) or EF tries and fails to start a distributed transaction, even if I follow the advice about opening a connection first.
This is really frustrating, because it is such a trivial thing to do with plain old SQL: begin transaction, SELECT, INSERT, commit transaction. How can I get this to work in EF? It doesn't have to be using transactions - whatever makes it work.
The first statement (the only one which could cause the error you describe) will never fail if your DB has a UNIQUE constraint on Sid. Does it? It should. That's the only way to make sure that the sid is truly, globally unique.