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

发布于 2024-09-19 17:52:19 字数 732 浏览 16 评论 0 原文

根据 NProf 的说法,不鼓励使用隐式事务:

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 保留在显式事务中?或者读操作使用隐式事务是否可以接受?

According to NHProf, the use of implicit transactions is discouraged:

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

However, NHibernate LINQ returns an IQueryable<> when reading objects from the database, and this is lazily evaluated. I have this method in a repository:

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

The issue here is that the method will commit the transaction before data is evaluated. Is there a way to use the repository pattern and keep the IQueryable<> in an explicit transaction? Or is it acceptable for read operations to use implicit transactions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

呆萌少年 2024-09-26 17:52:19

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

The repository should not create a transaction. That's a responsibility of a separate layer (which depends on the application type).

五里雾 2024-09-26 17:52:19

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

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 之前,您必须将整个结果集检索到列表中。

I'd refactor this to allow external transaction control. The Repository cannot know the scope of the unit of work that various read/write calls are a part of unless the code that makes the calls tells it. Consider setting up a "unit of work" pattern: without revealing specific details of the data store implementation, allow objects that depend on Repository to specify that they are beginning, aborting or completing a "unit of work".

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

Your Repository would probably implement this by maintaining a private Dictionary of SQL transactions, each keyed to a UnitOfWork object (this can be as simple as an empty instantiable class, or it can provide framework-agnostic information about state or metrics). When performing a DB operation, your callers will first ask to begin a UoW, and they will be given a token that they will use to identify the context within which they are making a DB call. The object that gets the token can pass it to other classes that need to perform DB operations in the same operational context. The unit of work will remain open until the dependent class tells the Repository that it is finished, allowing lazy-loads and atomic multi-operation procedures.

Notice that there are overloads that do not require units of work. It is possible, and perhaps necessary, to make simple calls without explicitly starting a unit of work. In these cases, your Repository can create an internal UOW, perform the requested operation, and return the results. However, lazy-loading will be difficult or impossible in these cases; you will have to retrieve the entire result set into a List before ending the internal UoW.

带上头具痛哭 2024-09-26 17:52:19

我和迭戈一起讨论这个问题——存储库无法知道事务范围。

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

I'm with Diego on this one - repository can't know the transaction scope.

I also have a concern about returning IQueryable - as I understand it, it has a ton of extra query methods that might be very difficult to unit test. I prefer returning IEnumerable and encapsulate more complex queries in repository methods. Otherwise, you'll have to unit test all kinds of variations of queries against the output of GetAll().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文