如何实现适用于 EF 和 NHibernate 的工作单元 [英] How to implement Unit of Work that works with EF and NHibernate

查看:17
本文介绍了如何实现适用于 EF 和 NHibernate 的工作单元的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一个在 Entity Framework 4.1 和 NHibernate 中都可以使用的工作单元实现.在下面找到我的实现细节的骨架

I was working on a Unit of Work implementation that works both in Entity Framework 4.1 and NHibernate. Find below the skeleton of my implementation details

IUnitOfWork 定义

IUnitOfWork definition

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

IRepository 定义

IRepository definition

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

UoW 在 NHibernate 中的实现

Implementation of UoW in 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();
    }
}

Entity Framework 4.1 中的工作单元

Unit of Work in 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();
    }
}

使用 NHibernate 的存储库

Repository using 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 上工作的事务.但是当我开始针对 Repositories 编写复杂的 LINQ 查询时,NH 大部分时间都会失败.当 Repository 返回 NhQueryable 时,OrderBy 和 ToList 等某些功能会引发错误.

With this implementation I could get most of the features like saving, deleting, transaction working on both EF and NH. But when I start writing complex LINQ queries against Repositories NH fails most of the time. Some features like OrderBy and ToList throws errors when Repository is returning NhQueryable.

下面的代码是从 ASP.NET MVC 控制器调用的,我正在使用 StructureMap 向其中注入 IUnitOfWork 的实例.当注入 NHibernateUnitOfWork 时,当注入 SqlUnitOfWork 时,Where 条件没有像预期的那样工作.

In the following code is called from ASP.NET MVC controller to which I'm injecting instance of IUnitOfWork using StructureMap. When NHibernateUnitOfWork is injected Where condition does not get applied where as it works as expected when SqlUnitOfWork is injected.

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

推荐答案

如果不构建支持在 linq 之上的不同提供的解决方案,这将导致灾难.Linq 和 IQueryable 是有漏洞的抽象——每个 Linq 提供者都可以有自己的特性"和限制.此外,EF 本身通过IQueryable 的自定义扩展方法添加了一些逻辑(如EFv4.1 中的IncludeAsNoTracking).这些方法在内部将 IQueryable 转换为 ORM 特定的类.

As a side not building solution supporting different provides on top of the linq is way to disaster. Linq and IQueryable are leaky abstractions - each Linq provider can have its own "features" and limitations. Moreover EF itselfs adds some logic via custom extension methods for IQueryable (like Include or AsNoTracking in EFv4.1). These methods internally converts IQueryable to ORM specific classes.

如果你想有通用的解决方案,你必须放弃Linq并添加第三种模式来形成抽象.除了存储库和工作单元模式之外,您还需要自定义规范模式.通常,您将重新实现 NHibernate 的 Criteria API.

If you want to have universal solution you must abandon Linq and add third pattern to form the abstraction. In addition to Repository and Unit of Work patterns you need custom Specification pattern. Generally you will reimplement NHibernate's Criteria API.

这篇关于如何实现适用于 EF 和 NHibernate 的工作单元的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆