Entity Framework The context cannot be used while

2019-07-13 03:48发布

问题:

My unit of work class is mentioned below and I am using Ninject and I have tried injecting IUnitOfWork per request per thread scope, transient etc. but I am still getting error which is:

"Message":"An error has occurred.","ExceptionMessage":"The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.","ExceptionType":"System.InvalidOperationException

I get this error when i make two web API (get) calls at the same time using angularJS and it shows error at the point _context.Set<TEntity>().FirstOrDefault(match);

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private My_PromotoolEntities _uowDbContext = new My_PromotoolEntities();

    private Dictionary<string, object> _repositories;


    // Do it like this if no specific class file
    private GenericRepository<MysPerson> _personRepository;
    //private GenericRepository<MysDataSource> dataSourcesRepository;
    //private GenericRepository<MysCountry> countryMasterRepository;


    // Or like this if with specific class file.
    private DataSourceRepository _dataSourcesRepository;
    private CustomerRepository _customerRepository;
    private DeviceRepository _deviceRepository;
    private DeviceRegistrationRepository _deviceRegistrationRepository;
    private EmailQueueRepository _emailQueueRepository;


    public void SetContext(My_PromotoolEntities context)
    {
        _uowDbContext = context;
    }


    public void CacheThis(object cacheThis, string keyName, TimeSpan howLong)
    {
        Cacheing.StaticData.CacheStaticData(cacheThis, keyName, howLong);
    }
    public object GetFromCache(string keyName)
    {
        return Cacheing.StaticData.GetFromCache(keyName);
    }


    public GenericRepository<T> GenericRepository<T>() where T : BaseEntity
    {
        if (_repositories == null)
        {
            _repositories = new Dictionary<string, object>();
        }

        var type = typeof(T).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(GenericRepository<>);
            var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), _uowDbContext);
            _repositories.Add(type, repositoryInstance);
        }
        return (GenericRepository<T>)_repositories[type];
    }

    public GenericRepository<MysPerson> PersonRepository
    {
        get
        {
            if (this._personRepository == null)
            {
                this._personRepository = new GenericRepository<MysPerson>(_uowDbContext);
            }
            return _personRepository;
        }
    }
    public DataSourceRepository DataSourcesRepository
    {
        get
        {
            if (this._dataSourcesRepository == null)
            {
                this._dataSourcesRepository = new DataSourceRepository(_uowDbContext);
            }
            return _dataSourcesRepository;
        }
    }
    public CustomerRepository CustomerRepository
    {
        get
        {
            if (this._customerRepository == null)
            {
                this._customerRepository = new CustomerRepository(_uowDbContext);
            }
            return _customerRepository;
        }
    }
    public DeviceRepository DeviceRepository
    {
        get
        {
            if (this._deviceRepository == null)
            {
                this._deviceRepository = new DeviceRepository(_uowDbContext);
            }
            return _deviceRepository;
        }
    }
    public DeviceRegistrationRepository DeviceRegistrationRepository
    {
        get
        {
            if (this._deviceRegistrationRepository == null)
            {
                this._deviceRegistrationRepository = new DeviceRegistrationRepository(_uowDbContext);
            }
            return _deviceRegistrationRepository;
        }
    }

    public EmailQueueRepository emailQueueRepository
    {
        get
        {
            if (this._emailQueueRepository == null)
            {
                this._emailQueueRepository = new EmailQueueRepository(_uowDbContext);
            }
            return _emailQueueRepository;
        }
    }




    /// <summary>
    /// Commits all changes to the db. Throws exception if fails. Call should be in a try..catch.
    /// </summary>
    public void Save()
    {
        try
        {
            _uowDbContext.SaveChanges();
        }
        catch (DbEntityValidationException dbevex)
        {
            // Entity Framework specific errors:

            StringBuilder sb = new StringBuilder();
            var eve = GetValidationErrors();
            if (eve.Count() > 0)
            {
                eve.ForEach(error => sb.AppendLine(error));
            }

            ClearContext();

            // Throw a new exception with original as inner.
            var ex = new Exception(sb.ToString(), dbevex);
            ex.Source = "DbEntityValidationException";
            throw ex;
        }
        catch (Exception)
        {
            ClearContext();
            throw;
        }
    }

    private void ClearContext()
    {
        DetachAll();
    }

    private void DetachAll()
    {
        foreach (DbEntityEntry dbEntityEntry in _uowDbContext.ChangeTracker.Entries())
        {

            if (dbEntityEntry.Entity != null)
            {
                dbEntityEntry.State = EntityState.Detached;
            }
        }
    }

    /// <summary>
    /// Checks for EF DbEntityValidationException(s).
    /// </summary>
    /// <returns>Returns a List of string containing the EF DbEntityValidationException(s).</returns>
    public List<string> GetValidationErrors()
    {
        if (_uowDbContext.GetValidationErrors().Count() != 0)
        {
            return _uowDbContext.GetValidationErrors().Select(e => string.Join(Environment.NewLine, e.ValidationErrors.Select(v => string.Format("{0} - {1}", v.PropertyName, v.ErrorMessage)))).ToList();
        }
        return null;
    }



    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _uowDbContext.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

回答1:

You should never use a context in 2 places at the same time, that's exactly why you are getting this error. From the MSDN documentation:

Thread Safety: Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.



回答2:

It is a little hard to make suggestions without a repro but there is a brute force approach that should resolve the issue. If you have an interception point before/during DI setup then you can cause all the context initialization etc to happen by creating an instance of your context and calling ctx.Database.Initialize(force: false); Passing 'force: false' will ensure that the initialization still only happens once per AppDomain