实体框架在创建模型时不能使用上下文

时间:2015-10-15 10:24:35

标签: multithreading asp.net-web-api model-view-controller entity-framework-6

下面提到了我的工作单元类,我正在使用Ninject,我已经尝试为每个线程范围,瞬态等每个请求注入IUnitOfWork但是我仍然遇到错误:

  

"消息":"发生错误。"," ExceptionMessage":"创建模型时无法使用上下文。如果在OnModelCreating方法中使用上下文,或者同时由多个线程访问相同的上下文实例,则可能抛出此异常。请注意,DbContext和相关类的实例成员不保证是线程安全的。"," ExceptionType":" System.InvalidOperationException

当我使用angularJS同时进行两次web API(get)调用时出现此错误,并且它在_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);
    }
}

2 个答案:

答案 0 :(得分:2)

您不应该同时在2个地方使用上下文,这正是您收到此错误的原因。来自MSDN documentation

  

线程安全:此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。 不保证所有实例成员都是线程安全的。

答案 1 :(得分:0)

如果没有复制品,提出建议有点困难,但有一种蛮力方法可以解决问题。如果在DI设置之前/期间有一个拦截点,那么你可以通过创建一个上下文实例并调用ctx.Database.Initialize(force:false)来使所有上下文初始化等发生。传递'force:false'将确保初始化仍然只发生一次AppDomain