2011-09-14 1 views
15

私は、Entity Framework 4.1とNHibernateの両方で動作するUnit of Work実装に取り​​組んでいました。私の実装の詳細のスケルトンEFとNHibernateで動作するUnit of Workを実装する方法

IUnitOfWork定義作業の

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(); 
    } 
} 

単位のNHibernateにおけるUOWの

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); 
} 

実装Entity Frameworkの4.1

で下の検索この実装ではEntity Frameworkの

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); 
    } 
} 

を使用して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); 
    } 
} 

リポジトリを使用して10

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(); 
    } 
} 

リポジトリ私は、トランザクションがEFとNHの両方に取り組んで、削除、保存などの機能のほとんどを得ることができます。しかし、リポジトリに対して複雑なLINQクエリを書くとき、NHはほとんどの場合失敗します。 RepositoryがNhQueryableを返すときに、OrderByやToListなどのいくつかの機能がエラーをスローします。

次のコードは、StructureMapを使用してIUnitOfWorkのインスタンスを注入するASP.NET MVCコントローラから呼び出されます。 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とは、漏れやすい抽象であり、各Linqプロバイダは独自の「機能」と制限を持つことができます。さらに、EF自体は、IQueryable(EFv4.1ではIncludeまたはAsNoTrackingなど)のカスタム拡張メソッドを使用してロジックを追加します。これらのメソッドは内部でIQueryableをORM固有のクラスに変換します。

汎用ソリューションを使用する場合は、Linqを放棄し、抽象化を形成するために3番目のパターンを追加する必要があります。リポジトリおよび作業単位パターンに加えて、カスタムSpecificationパターンが必要です。一般的には、NHibernateのCriteria APIを再実装します。

6

IoCの観点から、エレガンスのための願いがあなたの道です。しかし、私がNHibernateのlinqプロバイダについて読んだのは、Linqプロバイダを最初に書くのは大変だから "beta-ish"だということです。だからここでバグに遭遇しているだけかもしれない。現在、私はLinq2Nhibernateで生産コードを書くことに非常に戸惑うでしょう。新しいQueryOver機能ははるかに強力です。もちろん、悲しいことに、QueryOverはあなたのアーキテクチャにシームレスには適合しません。なぜなら、Hibernateの構文をいつも使わなければならないからです。レポ以外の複雑なLinqクエリは、決してSQLに変換されないので役に立たないでしょう。

これは、最初はリポジトリにIQueryable<T>を返すことは役に立たないので、これはデザインの優雅さに対する死のキスです。しかし、IEnumerable<T>を返すと、あなたのEF実装が邪魔になります。だから、私は、両方の実装があまりにも単純で使いやすい汎用インターフェースの後ろに収まるように照会すると考えています。

Hereは、QueryOverとLinqの非常に便利な投稿です。

ところで、これは非常に興味深い質問とデザインです。私は複数の投票をすることができたらいいと思う!

2

Ladislavが言及しているQueryOverの技術的な問題に加えて、設計上の問題がある可能性があります。 Domain Driven Designの観点からRepositoryインターフェイスがUbiquitous Languageに基づいており、純粋なデータアクセスの概念であるIQueryableのようなものを公開していない場合、この問題は発生しません。このanswerには興味深い情報やリンクがあります。

関連する問題