linq - 如何实现与 EF 和 NHibernate 一起使用的工作单元

标签 linq nhibernate entity-framework-4 unit-of-work

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

IUnitOfWork 定义

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

NHibernate中UoW的实现
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 中的工作单元
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);
    }
}

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

通过这个实现,我可以获得大部分功能,例如在 EF 和 NH 上工作的保存、删除、事务。但是当我开始编写针对 Repositories NH 的复杂 LINQ 查询时,大部分时间都失败了。当 Repository 返回 NhQueryable 时,OrderBy 和 ToList 等功能会引发错误。

下面的代码是从 ASP.NET MVC Controller 调用的,我使用 StructureMap 将 IUnitOfWork 实例注入(inject)到该 Controller 中。当注入(inject) NHibernateUnitOfWork 时,在注入(inject) 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();

最佳答案

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

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

关于linq - 如何实现与 EF 和 NHibernate 一起使用的工作单元,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7411504/

相关文章:

c# - 是否有关于 Nhibernate 的概述,您必须按什么顺序使用扩展方法?

c# - Entity Framework 不会检测导航属性的更改

c# - 如何将此 LINQ 查询编写为单个查询?

c# - 将动态匿名对象添加到 linq groupBy 表达式中

c# - 过滤强类型结果

c# - 在哪些情况下我需要为 IEnumerable 和 IQueryable 创建两种不同的扩展方法?

c# - 使用 NHibernate Criteria API 选择特定的数据集和计数

nhibernate - 将对象从基类转换为子类是否有效?

c# - 这个查询是在内存中的集合上执行的,还是在数据库中执行的?

entity-framework - 使用EF4选择