Search code examples
entity-frameworktransactionsentity-framework-4.1transactionscope

Transactionally get or create DB object in Entity Framework


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.


Solution

  • 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.