2016-03-15 54 views
0

我試圖實現存儲庫模式,但遇到了與上下文有關的困難。當我開始調試我的UI顯示時,正如我所期望的那樣,所有的數據都會像它應該那樣返回,但是當我嘗試過濾數據時,我從EF那裏得到異常,通知我「已經有一個與此命令關聯的打開的DataReader ...」使用Unity時EF上下文不會處理

public static class UnityConfig 
{ 
    public static UnityContainer Container; 
    public static void RegisterComponents() 
    { 
     var container = new UnityContainer(); 
     Container = container; 

     // register all your components with the container here 
     // it is NOT necessary to register your controllers 

     container.RegisterType<DbContext, ApplicationDbContext>(new HierarchicalLifetimeManager()); 

     container.RegisterType<MyContext>(new PerRequestLifetimeManager()); 

     container.AddNewExtension<RepositoryModule>(); 

     DependencyResolver.SetResolver(new UnityDependencyResolver(container)); 
    } 
} 

public class RepositoryModule : UnityContainerExtension 
{ 
    protected override void Initialize() 
    { 
     Container.RegisterType<IBusinessClass>(
      new PerRequestLifetimeManager()), 
      new InjectionFactory(
       c => new BusinessClass 
        c.Resolve<IRepository<EntityOne>>(), 
        c.Resolve<IRepository<EntityTwo>>()); 
     ); 

     Container.RegisterType<IRepository<EntityOne>, RepoOne>(
      new PerRequestLifetimeManager(), 
      new InjectionFactory(
       c => new RepoOne(c.Resolve<MyContext>()))); 

     Container.RegisterType<IRepository<EntityTwo>, RepoTwo>(
      new PerRequestLifetimeManager(), 
      new InjectionFactory(
       c => new RepoTwo(c.Resolve<MyContext>())));   
    } 
} 

我的倉庫界面看起來是這樣的:

public interface IRepository<T> where T : class 
{ 

    IQueryable<T> Query { get; } 
    T GetByID(int id); 

    IEnumerable<T> GetAll(); 

    T Insert(T entity); 

    void Update(T entity); 

    void Delete(T entity); 

    void Save(); 
} 

存儲庫的實現使用抽象類的保存和處置功能:

public abstract class BaseRepository : IDisposable 
{  
    protected MyContext context; 

    protected BaseRepository(MyContext context) 
    { 
     this.context = context; 
    } 

    /// <summary> 
    /// Save changes 
    /// </summary> 
    public void Save() 
    { 
     context.SaveChanges(); 
    } 

    #region Dispose 

    private bool disposed = false; 

    protected virtual void Dispose(bool diposing) 
    { 
     if (!disposed) 
     { 
      context.Dispose(); 
     } 
    } 

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

    #endregion 
} 

個實際存儲庫是這樣的:

public class RepoOne : BaseRepository, IRepository<EntityOne> 
{ 
    public RepoOne(MyContext context) 
     : base(context) 
    { 
     this.context = context; 
    } 

    public IQueryable<EntityOne> Query => context.EntityOne; 

    public EntityOne GetByID(int id) 
    { 
     return context.EntityOne.Find(id); 
    } 

    public IEnumerable<EntityOne> GetAll() 
    { 
     return context.EntityOne.ToList(); 
    } 

    public EntityOne Insert(EntityOne entity) 
    { 
     return context.EntityOne.Add(entity); 
    } 

    public void Update(EntityOne entity) 
    { 
     var entityOne = context.EntityOne.Find(entity.ID); 
    } 

    public void Delete(EntityOne entity) 
    { 
     throw new NotImplementedException(); 
    } 
} 

我假設我的情況下是沒有得到安置,但我想不出爲什麼。我是否錯過了一些東西,或者我是否完全搞砸了這裏的建築?

[編輯] 該代碼正在處理第一次加載。只有當我回到存儲庫才能獲取我遇到的問題的過濾數據子集。

我已經更新瞭解決方案,用一個簡單的工廠,以獲得資源庫,所以在統一的任務現在看起來是這樣的:

  Container.RegisterType<IRepository<EntityOne>>(
      new PerRequestLifetimeManager(), 
      new InjectionFactory(
       c => 
        RepositoryFactory<EntityOne>.GetRepository(
         "EntityOne", 
         c.Resolve<MyContext>()))); 

和工廠:

public static class RepositoryFactory<T> where T : class 
{ 
    public static IRepository<T> GetRepository(string entityType,   PollBookMonitorContext context) 
    { 
     switch (entityType) 
     { 
      case "EntityOne": 
        return (IRepository<T>) new RepoOne(context); 
      case "EntityTwo": 
        return (IRepository<T>) new RepoTwo(context); 
      default: return null; 
     } 

@vendettamit:我的PerRequestLifetimeManager

public class PerRequestLifetimeManager : LifetimeManager 
{ 
    private readonly object key = new object(); 

    public override object GetValue() 
    { 
     if (HttpContext.Current != null && 
      HttpContext.Current.Items.Contains(key)) 
      return HttpContext.Current.Items[key]; 

     return null; 
    } 

    public override void SetValue(object newValue) 
    { 
     if (HttpContext.Current != null) 
      HttpContext.Current.Items[key] = newValue; 
    } 

    public override void RemoveValue() 
    { 
     if (HttpContext.Current != null) 
      HttpContext.Current.Items.Remove(key); 
    } 
} 

任何幫助,非常感謝。

我的代碼終於工作了。我剛剛發現,我查詢的其中一個表格是整天都在處理大量數據,並且據我所知,這對EF從數據庫中讀取的方式造成了不利影響。或者,當我不注意時,我可能會解決這個問題。

+0

你確定這是不是處置? – Greg

+0

我假設異常消息指出已經有一個與命令關聯的打開的數據讀取器意味着上下文尚未處理。 – crunchy

+1

@crunchy不需要處理......問題是指在同一個上下文中同時查詢多個數據(也許兩個不​​同的http請求獲取相同的上下文對象?)...生命週期上下文對象不是很重要(你可以不經過處理就傳遞),實際查詢的生命週期是(特別是在多線程場景中)。你確定你的'PerRequestLifetimeManager'實現正在做它應該做的事嗎? – Jcl

回答

1

我覺得現在的問題是在這裏:

private bool disposed = false; 

protected virtual void Dispose(bool diposing) 
{ 
    if (!disposed) 
    { 
     context.Dispose(); 
    } 
} 

在相反,如果利用安裝在全球領域,使用參數「處置」

protected virtual void Dispose(bool diposing) 
{ 
     if (!disposing) return;   
     context.Dispose(); 
} 
+0

這沒有幫助,我可以看到。謝謝 – crunchy

1

使用PerRequestLifetimeManager需要註冊UnityPerRequestHttpModule。據MSDN

對於註冊類型的實例被在HTTP請求完成時自動設置,確保與該web應用程序註冊的UnityPerRequestHttpModule。

檢查UnityConfig.cs,確保它沒有註冊模塊。模塊註冊應在Unity.Mvc.Activator.cs中完成。或者你也可以嘗試註冊在啓動文件模塊(OWIN):

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(UnityWebActivator), "Start")]

+0

我自己編寫了PerRequestLifetimeManager,所以它不應該需要註冊,因爲我在RegisterComponents方法中「新建」了它(我認爲?) – crunchy

+0

該類適用於上述模塊。查看詳情https://unity.codeplex.com/SourceControl/latest#source/Unity.Mvc/Src/PerRequestLifetimeManager.cs – vendettamit

+0

我不確定你的意思。我寫了一個自定義的PerRequestLifetimeManager,因此註冊Unity模塊不會影響我的代碼。 – crunchy

1

你應該做到以下幾點:

public interface IRepository : IDisposable 
{ 
    // Your code. 
} 

然後在數據方面,將實現你的配置:

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

protected virtual void Dispose(bool disposing) 
{ 
    if(!disposed) 
    { 
      if(disposing) 
       component.Dispose(); 

      disposed = true; 
    } 
} 

~DataContext() { Dispose(false); } 

但是,如果您真的想要,您可以清理一下您的架構。你可以做的一種方法,是用這種方式:

public interface IRepository 
{ 
    // Your method operation. 
} 

public interface IFactory : IRepositoryFactory 
{ 
    // Container factory, to interject between multiple data context. 
} 

public interface IRepositoryFactory 
{ 
    IRepository Create(); 
} 

public class DataContext : DbContext, IRepository 
{ 
    // Entity Framework and Repository concreete implementation. 
} 

public class DataContextFactory : IFactory 
{ 
    public IRepository Create() 
    { 
     return new DataContext(); 
    } 
} 

那麼你將映射與團結的工廠,那麼只需在一個方法中調用:

using(var context = Create()) 
    return context.List<Model>(....);