2011-09-14 30 views
15

Tôi đã làm việc về triển khai Đơn vị công việc hoạt động cả trong Khuôn khổ thực thể 4.1 và NHibernate. Tìm thấy bên dưới bộ xương của chi tiết thực hiện của tôiCách triển khai Đơn vị công việc hoạt động với EF và NHibernate

IUnitOfWork nét

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

nét 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); 
} 

Thực hiện UOW trong NHibernate

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

Đơn vị làm việc trong Entity Framework 4.1

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

Repository sử dụng 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); 
    } 
} 

Repository sử dụng 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); 
    } 
} 

Với thi này, tôi có thể nhận được hầu hết các tính năng như tiết kiệm, xóa, giao dịch làm việc trên cả EF và NH. Nhưng khi tôi bắt đầu viết các truy vấn LINQ phức tạp đối với Repositories NH thì phần lớn thời gian. Một số tính năng như OrderBy và ToList ném lỗi khi Repository trở về NhQueryable.

Trong mã sau đây được gọi là từ bộ điều khiển ASP.NET MVC mà tôi đang tiêm dụ của IUnitOfWork sử dụng StructureMap. Khi NHibernateUnitOfWork được tiêm Trường hợp điều kiện không được áp dụng khi nó hoạt động như mong đợi khi SqlUnitOfWork được tiêm.

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

Khi trừu tượng hóa dữ liệu, điều này có thể được quan tâm cũng http://stackoverflow.com/a/12913174/671619 – Firo

Trả lời

14

Là một giải pháp xây dựng bên không hỗ trợ các cung cấp khác nhau trên đầu trang của linq là cách để khắc phục thảm họa. LINQ và IQueryable là trừu tượng bị rò rỉ - mỗi nhà cung cấp LINQ có thể có "tính năng" và hạn chế riêng của nó. Hơn nữa bản thân EF bổ sung thêm một số logic thông qua các phương pháp mở rộng tùy chỉnh cho IQueryable (như Include hoặc AsNoTracking trong EFv4.1).Các phương thức này chuyển đổi nội bộ IQueryable thành các lớp cụ thể ORM.

Nếu bạn muốn có giải pháp chung, bạn phải từ bỏ LINQ và thêm mẫu thứ ba để tạo thành trừu tượng. Ngoài các mẫu Kho lưu trữ và Đơn vị công việc, bạn cần tùy chỉnh mẫu Specification. Nói chung, bạn sẽ reimplement API tiêu chuẩn NHibernate.

6

Từ quan điểm của IoC và mong muốn sang trọng theo cách của bạn là cách để đi. Tuy nhiên, tất cả tôi đọc về nhà cung cấp LINQ NHibernate là nó vẫn còn "beta-ish", bởi vì nó là như vậy damn khó để viết các nhà cung cấp LINQ ở nơi đầu tiên. Vì vậy, nó cũng có thể là bạn chỉ cần chạy vào một lỗi ở đây. Hiện tại tôi sẽ rất miễn cưỡng khi viết mã sản xuất với Linq2Nhibernate. Tính năng QueryOver mới mạnh hơn rất nhiều. Nhưng tất nhiên, thật đáng buồn, QueryOver không phù hợp với kiến ​​trúc của bạn, bởi vì bạn sẽ phải sử dụng cú pháp NHibernate tất cả các cách. Các truy vấn LINQ phức tạp bên ngoài repo của bạn sẽ vô ích bởi vì chúng sẽ không bao giờ được dịch sang SQL.

Tôi sợ điều này có hiệu quả là nụ hôn của cái chết cho sự sang trọng của thiết kế của bạn, bởi vì, để bắt đầu, nó sẽ là vô ích để cho một kho lưu trữ trở lại một IQueryable<T>. Nhưng việc trả lại IEnumerable<T> sẽ làm tê liệt việc thực thi EF của bạn. Vì vậy, những gì được boils xuống, tôi nghĩ rằng để truy vấn cả hai triển khai quá khác nhau để phù hợp với một giao diện chung gọn gàng.

Here là một bài đăng rất hữu ích trên QueryOver và LINQ.

BTW: đây là một câu hỏi và thiết kế rất thú vị. Tôi ước tôi có thể cho nhiều hơn một phiếu bầu!

2

Ngoài những khó khăn kỹ thuật với QueryOver được Ladislav đề cập, có thể có vấn đề về thiết kế. Bạn sẽ không gặp vấn đề này nếu bạn tiếp cận nó từ góc độ Domain Driven Design nơi giao diện Repository dựa trên Ubiquitous Language và không phơi bày những thứ như IQueryable là một khái niệm truy cập dữ liệu thuần túy. Điều này answer có thông tin và liên kết mà bạn có thể thấy thú vị.

Các vấn đề liên quan