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.
Related
So I'm hitting my webservice to save a row in the database if the id doesn't already exist. So I do this in two steps. First I run a select on the context to bring back the entity with the given id. If that returns null, I create a new entity and call saveChanges. This works perfectly almost always. However, when there are multiple requests hitting the webserver at the same time, there is a chance that 2 requests could run at pretty much the same time. When this happens, there is the chance that they both create a row. This is actually happening.
How would you handle this situation?
p.s. I'm using EntityFramework Core.
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”.
I am developing an application in C# by using the ADO.NET Entity Framework.
In many examples on the web I see that in order to add an element, newProduct, to an entity, let's assume Product, it is used the following code:
context.Products.Add(newProduct);
The method Add, however, it is not a member of Products so I cannot use it. Maybe the EF used in the examples is LinqToSQL.
However in ADO.NET there is a method AddObject:
context.AddObject("Products", newProduct)
and it works but I don't like it for two reasons:
1) I try to avoid as much as possible magic strings unless they are really the only resort to implement a functionality
2) It gives void as a return type, how can I check whether the insert was good or not?
I believe there is another way to insert my entities. Anybody might help?
1) I try to avoid as much as possible magic strings unless they are
really the only resort to implement a functionality
If context is an ObjectContext (EF <= 4.0), you should normally have a member in your derived context which represents the ObjectSet<Product> with the name Products or similar. You can use then:
context.Products.AddObject(newProduct);
Even if you don't have such a set in your context there is another strongly-typed option:
context.CreateObjectSet<Product>().AddObject(newProduct);
2) It gives void as a return type, how can I check whether the insert
was good or not?
AddObject does not perfrom the INSERT into the database at all. It only puts the object into Added state in the objectContext. The real INSERT happens later in a single transaction when you call SaveChanges.
Nonetheless, AddObject can fail, maybe if you add two objects with the same key into the context (if your entity does not have autogenerated identities for the key), or for other reasons. If so, AddObject will throw an exception which you shouldn't catch because it usually indicates a serious problem or bug in your code.
SaveChanges returns an int. But this int does not indicate that SaveChanges and inserting your object was successful. It only counts the number objects which are in Added state (will cause INSERT statement), in Modified state (will cause UPDATE statement) and in Deleted state (will cause DELETE statement) in the object context before the SQL statements get exceuted.
Again, if any of the SQL statements (like your INSERT) was not successful, SaveChanges will throw an exception. The exception can indicate problems already on client side or it can tell you that a problem during a SQL operation occured - for example: For a failed INSERT the exception might give you a message that the INSERT failed because there was already a row with the key you want to insert in the database or that required non-nullable columns are not filled in the entity you want to insert, etc. Also exceptions due to concurrency issues are possible among a lot of other exception types.
You can check if SaveChanges succeeded by catching possible exceptions:
try
{
int numberOfObjects = context.SaveChanges();
}
catch (SomeExceptionType e)
{
// What now?
}
BTW: The context.Products.Add(...) you've seen was most likely an example where context is a DbContext (EF >= 4.1). DbContext is a simplified API to Entity Framework (which still uses the core ObjectContext under the covers). In this API the method to insert a new entity is indeed called Add (method of DbSet<T>) and not AddObject.
I think I'm running into a common problem:
I would like to try to insert an object to the database. If primary key is violated then I would like to abort the insert. (this is an example, the question really applies to any kind of error and any of the CRUD operations)
How can I discard changes made to EF context?
I can't afford recreating it every time something goes wrong.
PS. I know that perhaps I could check if everything is ok eg. by querying the db, but I don't like the idea. Db constraints are there for some reason and this way it's faster and I have to write less code.
You can detach inserted entity from ObjectContext. You can also use ObjectStateManager and its method GetObjectStateEntries. In ObjectStateEntry you can modify its state.
The problem is that you are not using technology in supposed way:
I can't afford recreating it every
time something goes wrong.
Sure you should because your code doesn't prevent such situations.
PS. I know that perhaps I could check
if everything is ok eg. by querying
the db, but I don't like the idea. Db
constraints are there for some reason
and this way it's faster and I have to
write less code.
Yes indeed you should check if everything is OK. Calling database to "validate" your data is something that DBAs really like (sarcasm). It is your responsibility to achieve the highest possible validity of your data before you call SaveChanges. I can imagine that many senior developers / team leaders would simply not pass your code through their code review. And btw. in the most cases it is not faster because of inter process or network communication.
Try using DbTransaction.
System.Data.Common.DbTransaction _tran = null;
_tran = _ent.Connection.BeginTransaction();
_tran .Commit (); //after SaveChanges();
and if theres an exception
do a rollback.
_tran.Rollback();
A have a JPA entity that has timestamp field and is distinguished by a complex identifier field. What I need is to update timestamp in an entity that has already been stored, otherwise create and store new entity with the current timestamp.
As it turns out the task is not as simple as it seems from the first sight. The problem is that in concurrent environment I get nasty "Unique index or primary key violation" exception. Here's my code:
// Load existing entity, if any.
Entity e = entityManager.find(Entity.class, id);
if (e == null) {
// Could not find entity with the specified id in the database, so create new one.
e = entityManager.merge(new Entity(id));
}
// Set current time...
e.setTimestamp(new Date());
// ...and finally save entity.
entityManager.flush();
Please note that in this example entity identifier is not generated on insert, it is known in advance.
When two or more of threads run this block of code in parallel, they may simultaneously get null from entityManager.find(Entity.class, id) method call, so they will attempt to save two or more entities at the same time, with the same identifier resulting in error.
I think that there are few solutions to the problem.
Sure I could synchronize this code block with a global lock to prevent concurrent access to the database, but would it be the most efficient way?
Some databases support very handy MERGE statement that updates existing or creates new row if none exists. But I doubt that OpenJPA (JPA implementation of my choice) supports it.
Event if JPA does not support SQL MERGE, I can always fall back to plain old JDBC and do whatever I want with the database. But I don't want to leave comfortable API and mess with hairy JDBC+SQL combination.
There is a magic trick to fix it using standard JPA API only, but I don't know it yet.
Please help.
You are referring to the transaction isolation of JPA transactions. I.e. what is the behaviour of transactions when they access other transactions' resources.
According to this article:
READ_COMMITTED is the expected default Transaction Isolation level for using [..] EJB3 JPA
This means that - yes, you will have problems with the above code.
But JPA doesn't support custom isolation levels.
This thread discusses the topic more extensively. Depending on whether you use Spring or EJB, I think you can make use of the proper transaction strategy.