2014-09-19 70 views
4

我目前正在編寫一個依賴於數據庫的應用程序,而且我正在使用Entity Framework(根據Nuget 6.1.1版)。C#通用倉庫 - 工作單元 - 線程安全

現在我已經寫了一個存儲庫模式,它看起來像以下:

public class RepositoryBase<TEntity> where TEntity : class 
{ 
    #region Constructors 

    protected RepositoryBase(IDbContext context, IUnitOfWork unitOfWork) 
    { 
     Context = context; 
     DbSet = Context.Set<TEntity>(); 
     UnitOfWork = unitOfWork; 
    } 

    #endregion 

    #region Properties 

    protected IDbSet<TEntity> DbSet; 

    protected readonly IDbContext Context; 

    protected readonly IUnitOfWork UnitOfWork; 

    #endregion 

    #region Methods 

    protected TEntity Get(Expression<Func<TEntity, bool>> filter) 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     return !query.Any() ? null : !query.Where(filter).Any() ? null : query.First(filter); 
    } 

    protected TEntity Get(Expression<Func<TEntity, bool>> filter, string[] includeProperties) 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     includeProperties.Each(x => query = query.Include(x)); 

     return !query.Any() ? null : !query.Where(filter).Any() ? null : query.First(filter); 
    } 

    protected virtual IQueryable<TEntity> GetAll() 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     return query.AsQueryable(); 
    } 

    protected IQueryable<TEntity> GetAll(string[] includeProperties) 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     includeProperties.Each(x => query = query.Include(x)); 

     return query.AsQueryable(); 
    } 

    protected IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> filter) 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     query = DbSet.Where(filter); 

     return query; 
    } 

    protected IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> filter, string[] includeProperties) 
    { 
     DbSet.ThrowIfNull("DbSet"); 

     IQueryable<TEntity> query = DbSet; 

     query = DbSet.Where(filter); 

     includeProperties.Each(x => query = query.Include(x)); 

     return query; 
    } 

    #endregion 
} 

我確實有從這個庫繼承的類,但我會離開他們超出範圍的時刻。現在,當我做一個存儲庫上的獲取,我檢索數據,但是當我執行2獲取呼叫在sime時間(我只是打開2個瀏覽器窗口與相同的url並執行它們),然後下面的錯誤仍彈出:

類型的異常「System.Data.Entity.Core.EntityException」 發生EntityFramework.SqlServer.dll但在用戶 代碼

附加信息沒有被處理的:底層提供商在Open上失敗。

這裏有一個小圖像,以提供儘可能詳細的資料可能:

enter image description here

現在,我知道,在默認情況下實體Framwork的DbContext不是線程安全的,所以我知道我應該做一些事情來使其線程安全,但我只是不知道如何做到這一點。

可以anybode提供我的任何指導如何實現這一目標? 另外,我對IDisposeable沒有太多經驗,所以如果你建議在某個地方實現這一點,那麼你應該非常友好地向我提供一些關於如何實現這一點的信息。

重要的東西要提的是,我使用的是統一管理我的對象,你可以在這裏找到的配置:

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

我的類型的註冊都是在這裏完成:

public static void RegisterTypes(IUnityContainer container) 
{ 
    container.RegisterType<IDbContext, OxygenDataContext>(); 
    container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager()); 
} 

類'OxygenDataContext'是我實現'DbContext'和'IDbContext'的地方。 該代碼可以發現如下(一剪掉,我不發表我的所有元素):

public class OxygenDataContext : DbContext, IDbContext 
{ 
    #region Constructors 

    public OxygenDataContext() 
     : base("Oxygen") 
    { 
     Configuration.ProxyCreationEnabled = false; 
    } 

    #endregion 

    #region IDbContext Members 

    public IDbSet<TEntity> Set<TEntity>() where TEntity : class 
    { 
     return base.Set<TEntity>(); 
    } 

    public void SaveChanges() 
    { 
     base.SaveChanges(); 
    } 

    #endregion 
} 

更新

據我,這是團結,因爲我這是造成這種現象使用:

container.RegisterType<IDbContext, OxygenDataContext>(); 

這意味着每個請求都不會創建OxygenDataContext的新實例。

感謝您的幫助。

更新2

當我修改統一confiruation類似如下:

container.RegisterType<IDbContext, OxygenDataContext>(new PerRequestLifetimeManager()); 

我因子評分會有隻有1單一實例,但似乎它不工作,因爲我一直在接收到相同的錯誤。

更新3

我已經改變了統一配置,以解決IDbContext,這是一個的DbContext用「PerResolveLifetimeManager」使用下面的代碼:

container.RegisterType<IDbContext, OxygenDataContext>(new PerResolveLifetimeManager()); 

這意味着,每解決呼叫應該創建'OxygenDataContext'的新實例或我不正確?

在我的UnitOfWork的構造函數中,我指定的倉庫,如:

/// <summary> 
///  Creates a new instance of the <see cref="IUnitOfWork"/>. 
/// </summary> 
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param> 
public UnitOfWork(IDbContext context) 
    : base(context) 
{ 
    versioningRepository = new Repository<Versioning, IDbContext>(context, this); 

    settingRepository = new Repository<Setting, IDbContext>(context, this); 
    siteRepository = new VersionedRepository<Site, int, IDbContext>(context, this); 
    pageRepository = new VersionedRepository<Page, int, IDbContext>(context, this); 
    layoutRepository = new VersionedRepository<Layout, int, IDbContext>(context, this); 
    assemblyRepository = new VersionedRepository<Assembly, int, IDbContext>(context, this); 
    logRepository = new Repository<Log, IDbContext>(context, this); 
} 

所以這應該意味着,每一個信息庫獲得「IDbContext」的它自己唯一的實例,但它不按預期工作。

更新4

我已經成功地解決我自己的問題,因爲我寫的溶液,作爲一個答案後耐心等待。

更新5

這似乎並沒有工作。看到這裏我的解決方案,我認爲是工作:

我適應我的IUnitOfWork接口來實現IDisposeable接口,比重新調整執行以下:

protected virtual void Dispose(bool disposing) 
{ 
    if (disposing) { Context = null; } 
} 

public void Dispose() 
{ 
    Dispose(true); 
} 

但是,這是行不通的。我變得有點丟在這裏,因此,如果有人知道:-)

更新6

根據一些帖子,我需要提供有關如何,我實例化我的倉庫的一些信息的解決方案。 因此,這裏的解釋:

我使用一個包含所有倉庫一個的UnitOfWork:

/// <summary> 
///  Creates a new instance of the <see cref="IUnitOfWork"/>. 
/// </summary> 
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param> 
public UnitOfWork(IDbContext context) 
    : base(context) 
{ 
    versioningRepository = new Repository<Versioning>(context, this); 

    settingRepository = new Repository<Setting>(context, this); 
    siteRepository = new VersionedRepository<Site, int>(context, this); 
    pageRepository = new VersionedRepository<Page, int>(context, this); 
    layoutRepository = new VersionedRepository<Layout, int>(context, this); 
    assemblyRepository = new VersionedRepository<Assembly, int>(context, this); 
    logRepository = new Repository<Log>(context, this); 
} 

我也試圖改變代碼以下,但無法正常工作或:

/// <summary> 
///  Creates a new instance of the <see cref="IUnitOfWork"/>. 
/// </summary> 
/// <param name="context">The <see cref="IDbContext"/> in which the entities are available.</param> 
public UnitOfWork(IDbContext context) 
    : base(context) 
{ 
    versioningRepository = new Repository<Versioning>(context, this); 

    settingRepository = new Repository<Setting>(new OxygenDataContext(), this); 
    siteRepository = new VersionedRepository<Site, int>(new OxygenDataContext(), this); 
    pageRepository = new VersionedRepository<Page, int>(new OxygenDataContext(), this); 
    layoutRepository = new VersionedRepository<Layout, int>(new OxygenDataContext(), this); 
    assemblyRepository = new VersionedRepository<Assembly, int>(new OxygenDataContext(), this); 
    logRepository = new Repository<Log>(new OxygenDataContext(), this); 
} 

我仍然收到錯誤消息:「基礎提供開放式失敗」與內部異常,顯示「連接沒有關閉連接的當前狀態連接。「

你看到的UnitOfWork的構造確實需要一個IDbContext實例和所有的倉庫都與同樣的實例構造。

我使用的是團結,我IDbContext的註冊採取以下代碼:

container.RegisterType<IDbContext, OxygenDataContext>(); 

在我的統一配置,我也做註冊IUnitOfWork接口:

container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager()); 

我有感覺ŧ該錯誤是在Unity註冊或UnitOfWork中的某處,但我似乎沒有找到它。

+0

有什麼內部異常?你的圖像很模糊,很難讀出內部的異常。 – Michael 2014-09-19 09:17:36

+0

事實上,圖像有點模糊。內部例外情況是:「連接未關閉,連接的當前狀態正在連接。」 – Complexity 2014-09-19 09:19:17

+0

難道你不是偶然在共享線程之間的**庫**嗎?因爲你使用DbContext註冊是可以的。你如何解決存儲庫本身? – 2014-09-19 09:46:17

回答

6

如果我正確地得到您的消息,您在線程之間共享相同的DbContext。這不是DbContext的意圖。以這種方式共享數據非常困難。

我不喜歡以「但你爲什麼需要它」的形式給出答案,但這篇文章看起來就像那樣。

我建議你重新設計解決方案,並保持DbContext對象的短命 - 做一個操作並處理DbContext。執行一個工作單元,提交/回滾並在存儲庫中完成。不要忘記DbContext會跟蹤它提取的所有對象。您可能不希望跟蹤的對象在內存中比在其中使用它們的事務活得更長。

讓數據庫成爲共享來自多個線程的數據的要點。

+0

感謝您的回答。我知道共享上下文是一個壞主意,但我坐在微軟Unity之間,似乎只對我起作用:) – Complexity 2014-09-19 09:34:40

+0

我明白了。我使用的一種方法是使存儲庫,其中T是IDbContext。然後Repository 實例化T.這樣,IDbContext的生命週期等於Repository 對象的生命週期。 – 2014-09-19 09:35:44

+0

我用更新2更新了我的問題,這樣現在Unity可以在'PerRequestLifetimeManager'上解析我的DbContext,但它似乎不起作用。我確實需要這樣的東西來使得單元測試變得簡單...我只是不明白爲什麼Unity不管理這類東西...... – Complexity 2014-09-19 09:37:05

1

這有可能是你沒有完全處置此背景:

protected virtual void Dispose(bool disposing) 
{ 
    if (disposing) { Context = null; } 
} 

public void Dispose() 
{ 
    Dispose(true); 
} 

我的理解是,當你創建的DbContext的抽象(如您的UOW)要明確調用垃圾收集器,以保證你已經清理了未使用的資源。另外,你需要處理dbContext而不是將它設置爲null。

例子:

private bool disposed = false; 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
       context.Dispose(); // <-- dispose 
     } 
     this.disposed = true; 
    } 

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

請試試這個:) - 希望它幫助。

+0

它似乎沒有幫助。我覺得我錯過了一些重要的代碼,但我不太確定它在哪裏。也許我最好的選擇是從scrath重寫代碼。我厭倦了尋找這個問題... – Complexity 2014-09-26 06:44:51

+0

對不起,沒有工作:(。問題是,你沒有正確處置你的dbContext,我的猜測是,因爲你從來沒有在你的代碼中的新上下文實例。據推測,這應該通過注入來處理,但試着用混凝土替換IDbContext,看看你是否仍然有問題。 – wahwahwah 2014-09-27 03:47:46

+0

你想在哪裏替換那個代碼?在工作單元中? – Complexity 2014-09-27 08:02:05

1

問題在於如何註冊IDbContext實例。 的DbContext不是線程安全的,所以你應該每個請求/線程一次解決它(和你一樣做倉庫和的UnitOfWork),所以更改您的註冊代碼:

container.RegisterType<IDbContext, OxygenDataContext>(new PerRequestLifetimeManager()); 
+0

這似乎也不起作用。我認爲這個瑕疵是在別的地方,但我只是無法追查到它... – Complexity 2014-09-26 16:50:51

+0

它沒有幫助的原因是,你有多個問題相互分層,現在你已經設置了正確的IoC,向我們展示一個實際使用IUnitOfWork的代碼。 – t3z 2014-09-27 18:57:13