1

Consider this scenario:

The repository:

public void Save(object entity) {
    lock (static object) {
        context.Add(entity);
        context.SaveChanges();
    }
}

The application:

//somewhere
new Thread(() => Repository.Save(entity)).Start()
//...

This "action" occurs several times. Randomly the db context raises an AccessViolationException during saving operation (unhandled exception despite the try-catch o.0).

I have already read that this kind of exception can be caused by a concurrent access on the context object.

The question is: why does this exception occur in my case? Note that the lock should ensure the thread-safe access. How can I resolve this problem?

I have an idea... I think that the context.SaveChanges is not "blocking". When a thread commits the saving another thread enters in the Save method causes a concurrency access...

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
user2957271
  • 182
  • 3
  • 19
  • Do you use the lock to synchronize other access, or just Save? – ta.speot.is Apr 26 '14 at 07:21
  • Each access on the repository is sync by a lock on a repository object class member...the save function performs two calls on the repository and it's sync by another object – user2957271 Apr 26 '14 at 07:30
  • So you don't protect against two different threads reading and writing? Also, *(unhandled exception despite the try-catch o.0)* There's no `try/catch` in your code and wrapping `new Thread` in a `try/catch` block won't catch any exceptions on that thread. – ta.speot.is Apr 26 '14 at 07:32
  • the try-catch is omitted (it catches all the Save method). If i wrap new Thread(() => Repository.Save(entity)).Start() the exception is not blocked – user2957271 Apr 26 '14 at 07:40
  • Is this a large database? I ask, because when it's small (as in numbers of records) it may actually no be a bad idea at all to access it through a singleton context and deal with thread safety in this one context. (I never thought I would ever write an evil thing like this, but these thread unsafe databases require special treatment). – Gert Arnold Apr 26 '14 at 22:48
  • no...the database is large (~ 1 GB)... i have already implemented a unit of work-based design and all works well now...thank you :) – user2957271 May 01 '14 at 06:45

1 Answers1

1

What you are doing with your DbContext sounds wrong and will inevitably lead to problems as your application grows. The EntityFramework DbContext is designed as a Unit of Work. Create it, use it, dispose it; and definitely do not start doing things with it on different threads. A transaction cannot span multiple threads simultaneously - it just can't (or even if there was some funky way of wangling it, it shouldn't), every thread gets it's own transaction.

You need to rework things so that whenever you work with your DbContext you do it as:

using (var context = new MyContext())
{
   // 1. get entities
   // 2. work with entities
   // 3. save changes
}
// ALL IN A SINGLE THREAD

The EntityFramework itself will not prevent you from working in some other way, but as you are beginning to discover at some point things will break in a hard to diagnose way.

satnhak
  • 9,407
  • 5
  • 63
  • 81
  • first of all thank you :) Yes i agree with you...but why my own thread sync does not work? i agree with you about the concept of unit of work (create, perform work and dispose). But in my case the lock should ensure the thread safe access *confused* – user2957271 Apr 26 '14 at 08:05
  • Because you are not stopping entities joining the transaction from another thread are you? That save knows nothing about where the entities it is saving came from. The lock does nothing for thread saftey. – satnhak Apr 26 '14 at 08:52
  • ok...another question...if you manage the context as a unit of work foreach different operations you create a new context. But what about if you want to get an entity and in the next operation you want to delete/modify this entity? you cannot exec a db.Remove(entity) on a new DbContext beacuse the entity is unknown in the state of the new context. I'm not sure that recreate a context for each operation is the best approach. – user2957271 Apr 26 '14 at 09:56
  • Yes, recreating a context for each operation is definitely the best approach. The problem you have, I'm guessing, is that you have no separation between the objects you use for data access and the objects you show to the user. I think this is a standard problem people have when they start using EF, they have a single context, keep objects hanging around forever and then at some point after they've been developing for a while things start to go wrong. Don't worry, it happens to everyone; now you need to go back and write the thing properly! – satnhak Apr 26 '14 at 15:48