如何保持IQueryable<>在使用存储库模式的事务中?

时间:2010-09-15 19:42:52

标签: c# linq nhibernate transactions iqueryable

根据NHProf,不建议使用隐式交易:

http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

然而,当从数据库中读取对象时,NHibernate LINQ返回IQueryable<>,这是懒惰的评估。我在存储库中有这个方法:

public IQueryable<T> GetAll<T>()
{
    using (var transaction = _session.BeginTransaction())
    {
        var data = _session.Linq<T>();
        transaction.Commit();
        return data;
    }
}

这里的问题是该方法将在评估data之前提交事务。有没有办法使用存储库模式并将IQueryable<>保留在显式事务中?或者读操作是否可以使用隐式事务?

3 个答案:

答案 0 :(得分:5)

存储库应创建事务。这是一个单独的图层(取决于应用程序类型)的责任。

答案 1 :(得分:3)

我会重构这个以允许外部事务控制。除非进行调用的代码告诉它,否则存储库无法知道各种读/写调用所属的工作单元的范围。考虑设置“工作单元”模式:在不泄露数据存储实现的特定细节的情况下,允许依赖于存储库的对象指定它们正在开始,中止或完成“工作单元”。

public interface IRepository
{
   public UnitOfWork BeginUnitOfWork()

   public void CommitUOW(UnitOfWork unit)

   public void AbortUOW(UnitOfWork unit)

   public IQueryable<T> GetAll<T>(UnitOfWork unit)

   public List<T> GetAll<T>()

   public void Store<T>(T theObject, UnitOfWork unit)

   public void Store<T>(T theObject)
}

您的存储库可能通过维护一个私有的SQL事务字典来实现这一点,每个字典都键入一个UnitOfWork对象(这可以像一个空的可实例化类一样简单,或者它可以提供有关状态或度量的框架无关信息)。执行数据库操作时,您的呼叫者将首先要求开始使用UoW,并且将为他们提供一个令牌,用于识别他们进行数据库呼叫的上下文。获取令牌的对象可以将其传递给需要在同一操作上下文中执行数据库操作的其他类。工作单元将保持打开状态,直到依赖类告诉存储库它已完成,允许延迟加载和原子多操作过程。

请注意,存在不需要工作单元的重载。在没有明确启动工作单元的情况下进行简单调用是可能的,也许是必要的。在这些情况下,您的存储库可以创建内部UOW,执行请求的操作,并返回结果。但是,在这些情况下,延迟加载将是困难的或不可能的;在结束内部UoW之前,您必须将整个结果集检索到List中。

答案 2 :(得分:1)

我和Diego在这一个 - 存储库无法知道事务范围。

我还担心返回IQueryable - 据我所知,它有大量额外的查询方法,可能很难进行单元测试。我更喜欢返回IEnumerable并在存储库方法中封装更复杂的查询。否则,您将必须针对GetAll()的输出单元测试各种查询变体。