如何保留 IQueryable<>在使用存储库模式的事务中?
根据 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
保留在显式事务中?或者读操作使用隐式事务是否可以接受?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
存储库不应不创建事务。这是单独层的职责(取决于应用程序类型)。
The repository should not create a transaction. That's a responsibility of a separate layer (which depends on the application type).
我会重构它以允许外部事务控制。存储库无法知道各种读/写调用所属的工作单元的范围,除非进行调用的代码告诉它。考虑设置“工作单元”模式:在不透露数据存储实现的具体细节的情况下,允许依赖于存储库的对象指定它们正在开始、中止或完成“工作单元”。
您的存储库可能会通过维护一个私有的 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".
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.
我和迭戈一起讨论这个问题——存储库无法知道事务范围。
我还担心返回 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().