2011-09-14 49 views
15

我正在研究可在Entity Framework 4.1和NHibernate中工作的工作單元實現。找到我的實現細節骨架如何實現與EF和NHibernate一起工作的工作單元

IUnitOfWork定義UOW的NHibernate的實體框架4.1

public interface IUnitOfWork 
{ 
    IRepository<LogInfo> LogInfos { get; } 
    IRepository<AppInfo> AppInfos { get; } 
    void Commit(); 
    void Rollback(); 
} 

IRepository定義

public interface IRepository<T> where T : class, IEntity 
{ 
    IQueryable<T> FindAll(); 
    IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate); 
    T FindById(int id); 
    void Add(T newEntity); 
    void Remove(T entity); 
} 

實施工作

public class NHibernateUnitOfWork : IUnitOfWork, IDisposable 
{ 
    public ISession Session { get; private set; } 

    public NHibernateUnitOfWork(ISessionFactory sessionFactory) 
    { 
     _sessionFactory = sessionFactory; 
     Session = _sessionFactory.OpenSession(); 
     _transaction = Session.BeginTransaction(); 
    } 

    public IRepository<LogInfo> LogInfos 
    { 
     get 
     { 
      if (_logInfo == null) 
      { 
       _logInfo = new NHibernateRepository<LogInfo>(Session); 
      } 

      return _logInfo; 
     } 
    } 

    public void Commit() 
    { 
     if (_transaction.IsActive) 
      _transaction.Commit(); 
    } 
} 

單元下方

public class SqlUnitOfWork : IUnitOfWork 
{ 
    private readonly ObjectContext _context; 

    public SqlUnitOfWork() 
    { 
     _context = new ObjectContext(connectionString); 
     _context.ContextOptions.LazyLoadingEnabled = true; 
    } 

    private SqlRepository<LogInfo> _logInfo = null; 

    public IRepository<LogInfo> LogInfos 
    { 
     get 
     { 
      if (_logInfo == null) 
      { 
       _logInfo = new SqlRepository<LogInfo>(_context); 
      } 
      return _logInfo; 
     } 
    } 

    public void Commit() 
    { 
     _context.SaveChanges(); 
    } 
} 

庫使用NHibernate

public class NHibernateRepository<T> : IRepository<T> where T : class, IEntity 
{ 
    protected ISession Session; 

    public NHibernateRepository(ISession session) 
    { 
     Session = session; 
    } 

    public IQueryable<T> FindAll() 
    { 
     return Session.Query<T>(); 
    } 

    public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate) 
    { 
     return Session.Query<T>().Where<T>(predicate); 
    } 

    public T FindById(int id) 
    { 
     return Session.Get<T>(id); 
    } 

    public void Add(T newEntity) 
    { 
     Session.Save(newEntity); 
    } 

    public void Remove(T entity) 
    { 
     Session.Delete(entity); 
    } 
} 

庫使用實體框架

public class SqlRepository<T> : IRepository<T> where T : class, IEntity 
{ 
    protected ObjectSet<T> ObjectSet; 

    public SqlRepository(ObjectContext context) 
    { 
     ObjectSet = context.CreateObjectSet<T>(); 
    } 

    public IQueryable<T> FindAll() 
    { 
     return ObjectSet; 
    } 

    public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate) 
    { 
     return ObjectSet.Where(predicate); 
    } 

    public T FindById(int id) 
    { 
     return ObjectSet.Single(i => i.Id == id); 
    } 

    public void Add(T newEntity) 
    { 
     ObjectSet.AddObject(newEntity); 
    } 

    public void Remove(T entity) 
    { 
     ObjectSet.DeleteObject(entity); 
    } 
} 

有了這個實現我能得到大部分的功能,如保存,刪除,兩個EF和NH交易工作。但是,當我開始編寫針對存儲庫的複雜LINQ查詢時,大部分時間NH都會失敗。當Repository返回NhQueryable時,OrderBy和ToList等一些功能會引發錯誤。

以下代碼是從ASP.NET MVC控制器調用的,我使用StructureMap向其注入了IUnitOfWork的實例。當注入NHibernateUnitOfWork時,在條件沒有得到應用的地方,因爲當注入SqlUnitOfWork時它的工作方式和預期的一樣。

var query = from a in _unitOfWork.AppInfos.FindAll() 
      join l in _unitOfWork.LogInfos.FindAll() 
      on a.Id equals l.ApplicationId 
      where l.Level == "ERROR" || l.Level == "FATAL" 
      group l by new { a.Id, a.ApplicationName } into g 
      select new LogInfoSummaryViewModel() 
      { 
       ApplicationId = g.Key.Id, 
       ApplicationName = g.Key.ApplicationName, 
       ErrorCount = g.Where(i => i.Level == "ERROR").Count(), 
       FatalCount = g.Where(i => i.Level == "FATAL").Count() 
      }; 
return query.AsEnumerable(); 
+0

抽象數據訪問時,這也可能是有趣的http://stackoverflow.com/a/12913174/671619 – Firo

回答

14

作爲支持linq之上不同提供的一個方面的解決方案是災難。 Linq和IQueryable是抽象漏洞 - 每個Linq提供者都可以有自己的「特性」和限制。此外,EF本身通過自定義擴展方法爲IQueryable(如EFV4.1中的IncludeAsNoTracking)添加了一些邏輯。這些方法在內部將IQueryable轉換爲ORM特定的類。

如果你想擁有通用的解決方案,你必須放棄Linq並添加第三種模式來形成抽象。除了存儲庫和工作單元模式外,您還需要自定義Specification模式。通常你會重新實現NHibernate的Criteria API。

6

從IoC的觀點和優雅的願望你的方式是要走的路。但是,我所讀到的關於NHibernate的linq提供者的一點是,它仍然是「beta-ish」,因爲要首先編寫Linq提供程序非常困難。所以很可能你在這裏遇到了一個bug。目前我很不願意用Linq2Nhibernate編寫生產代碼。新的QueryOver功能更強大。但是,可悲的是,QueryOver不能無縫地融入到你的架構中,因爲你必須一直使用NHibernate語法。您的回購之外的複雜Linq查詢將毫無用處,因爲它們永遠不會被轉換爲SQL。

恐怕這對於你設計的優雅而言是有效的死亡之吻,因爲首先,讓一個存儲庫返回一個IQueryable<T>是沒有用的。但返回IEnumerable<T>會削弱您的EF實施。所以,我認爲查詢這兩個實現太不同了,不適合一個整潔的通用接口。

Here是QueryOver和Linq上非常有用的文章。

順便說一句:這是一個非常有趣的問題和設計。我希望我能給一個以上的投票!

2

除了Ladislav提到的QueryOver的技術困難之外,可能還有一個設計問題。如果從Domain Driven Design的角度來看,您不會遇到此問題,其中Repository接口基於Ubiquitous Language,並且不會公開諸如純粹數據訪問概念的IQueryable之類的東西。這answer有您可能會感興趣的信息和鏈接。

相關問題