4

I am writing a C# .NET4.5 Console application targeting the Entity Framework 6.1.3. I am using the Unit Of Work paradigm as follows:

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private readonly DataContext _context;
    private readonly List<object> _repositories = new List<object>();
    public UnitOfWork(DataContext context)
    {
        _context = context;
        _context.Configuration.LazyLoadingEnabled = false;
    }

    public IRepository<T> GetRepository<T>() where T : class
    {
        //try to get existing repository
        var repo = (IRepository<T>)_repositories.SingleOrDefault(r => r is IRepository<T>);
        if (repo == null)
        {
            //if not found, create it and add to list
            _repositories.Add(repo = new EntityRepository<T>(_context));
        }
        return repo;
    }

    public int Commit()
    {
        return _context.SaveChanges();
    }


    public bool AutoDetectChanges
    {
        get { return _context.Configuration.AutoDetectChangesEnabled; }
        set { _context.Configuration.AutoDetectChangesEnabled = value; }
    }

And my Repository like this:

   public class EntityRepository<T> : IRepository<T> where T: class 
    {
        protected readonly DbContext Context;
        protected readonly DbSet<T> DbSet;
    public EntityRepository(DbContext context)
    {
        Context = context;            
        DbSet = Context.Set<T>();
    }

    public IQueryable<T> All()
    {
        return DbSet;
    }
    ….. other functions….

    public virtual void Add(T entity)
    {        
        DbEntityEntry dbEntityEntry = Context.Entry(entity);
        if (dbEntityEntry.State != EntityState.Detached)
        {
            dbEntityEntry.State = EntityState.Added;
        }
        else
        {
            DbSet.Add(entity);
        }
    }
    }

I call these like this:

 var rep = _uow.GetRepository<TableOfPies>();
 rep.Add(Pie);
 _uow.Commit();

My Console application has multiple threads, each of which will at some point want to Update / Edit / Add to the same tables in my cloud-based SQL Server database.

I have implemented thread-safe code for my other code using locks but I am not aware of how I can make Entity thread-safe? Right now, I get the following error:

INNER EXCEPTION: New transaction is not allowed because there are other threads running in the session.

I have looked online and have not been able to find much about Entity and multi-threading. I have heard that Entity does not support multi-threaded applications but find that heard to believe. Any pointers would be greatly appreciated.

Blorgbeard
  • 101,031
  • 48
  • 228
  • 272
Ed Landau
  • 966
  • 2
  • 11
  • 24
  • I would note that multi-threading only improves _CPU-bound_ tasks. Since database tasks are typically I/O bound (even more so for "cloud" databases) multithreading may not help much, and may even be _worse_ if the overhead of multiple threads overpowers any benefit. – D Stanley Apr 26 '16 at 02:20
  • @DStanley That's not entirely true. I use multi-threading on I/O tasks all the time and find a huge benefit. The advantage is that while you're waiting for the reply to one query, you can be preparing and sending the next. Of course, that doesn't really depend on more than one thread. It's more like multi-tasking, although it often looks the same. I really like the new Task library in .NET for that. I code as if it's multi-thread, but the framework takes care of how many threads it actually uses. – Gabriel Luci Apr 26 '16 at 12:13

1 Answers1

6

The documentation for DataContext states:

Any instance members are not guaranteed to be thread safe.

And that's what I've experienced too. I've tried to do what you're doing and I've seen bizarre errors that backup the idea that it is not thread safe.

You will have to create a new DataContext instance in each thread.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Wow. That's an easy solution... I thought I'd have to go back to SQL :). One DataContext per thread. Nice. I will try. – Ed Landau Apr 26 '16 at 02:08