使用 SqlConnection / System.Transactions 的每个请求会话
我刚刚开始在一个项目中使用 Dapper,过去几年主要使用 NHibernate 和 EF 等 ORM。
通常,在我们的 Web 应用程序中,我们为每个请求实现会话,在请求开始时开始事务并在请求结束时提交事务。
当直接使用 SqlConnection / System.Transactions 时,我们是否应该做类似的事情?
StackOverflow 是如何做到的?
解决方案
采纳@gbn 和@Sam Safron 的建议,我没有使用事务。就我而言,我只执行读取查询,因此似乎没有真正要求使用事务(与我被告知的隐式事务相反)。
我创建了一个轻量级会话接口,以便我可以为每个请求使用一个连接。这对我来说非常有益,因为使用 Dapper 时,我经常需要创建一些不同的查询来构建一个对象,并且宁愿共享相同的连接。
确定每个请求的连接范围并对其进行处置的工作是由我的 IoC 容器 (StructureMap) 完成的:
public interface ISession : IDisposable {
IDbConnection Connection { get; }
}
public class DbSession : ISession {
private static readonly object @lock = new object();
private readonly ILogger logger;
private readonly string connectionString;
private IDbConnection cn;
public DbSession(string connectionString, ILogger logger) {
this.connectionString = connectionString;
this.logger = logger;
}
public IDbConnection Connection { get { return GetConnection(); } }
private IDbConnection GetConnection() {
if (cn == null) {
lock (@lock) {
if (cn == null) {
logger.Debug("Creating Connection");
cn = new SqlConnection(connectionString);
cn.Open();
logger.Debug("Opened Connection");
}
}
}
return cn;
}
public void Dispose() {
if (cn != null) {
logger.Debug("Disposing connection (current state '{0}')", cn.State);
cn.Dispose();
}
}
}
I've just started using Dapper for a project, having mostly used ORMs like NHibernate and EF for the past few years.
Typically in our web applications we implement session per request, beginning a transaction at the start of the request and committing it at the end.
Should we do something similar when working directly with SqlConnection / System.Transactions?
How does StackOverflow do it?
Solution
Taking the advice of both @gbn and @Sam Safron I'm not using transactions. In my case I'm only doing read queries so it seems there is no real requirement to use transactions (contrary to what I've been told about implicit transactions).
I create a lightweight session interface so that I can use a connection per request. This is quite beneficial to me as with Dapper I often need to create a few different queries to build up an object and would rather share the same connection.
The work of scoping the connection per request and disposing it is done by my IoC container (StructureMap):
public interface ISession : IDisposable {
IDbConnection Connection { get; }
}
public class DbSession : ISession {
private static readonly object @lock = new object();
private readonly ILogger logger;
private readonly string connectionString;
private IDbConnection cn;
public DbSession(string connectionString, ILogger logger) {
this.connectionString = connectionString;
this.logger = logger;
}
public IDbConnection Connection { get { return GetConnection(); } }
private IDbConnection GetConnection() {
if (cn == null) {
lock (@lock) {
if (cn == null) {
logger.Debug("Creating Connection");
cn = new SqlConnection(connectionString);
cn.Open();
logger.Debug("Opened Connection");
}
}
}
return cn;
}
public void Dispose() {
if (cn != null) {
logger.Debug("Disposing connection (current state '{0}')", cn.State);
cn.Dispose();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这就是我们所做的:
我们在名为
Current
的对象上定义一个名为DB
的静态变量。在我们的例子中,
InstantiateDB
返回一个 L2S 上下文,但是在您的如果是这样,它可能是一个开放的SQLConnection
或其他什么。在我们的应用程序对象上,我们确保连接在请求结束时关闭。
然后,在代码中需要访问数据库的任何地方,您只需调用
Current.DB
即可自动工作。由于所有#if DEBUG
内容,这也是单元测试友好的。我们不会在每个会话中启动任何事务,如果我们在会话开始时启动了任何事务,并且在会话开始时进行了更新,我们将遇到严重的锁定问题,因为锁直到结束才会被释放。
This is what we do:
We define a static called
DB
on an object calledCurrent
In our case
InstantiateDB
returns an L2S context, however in your case it could be an openSQLConnection
or whatever.On our application object we ensure that our connection is closed at the end of the request.
Then anywhere in your code where you need access to the db you simple call
Current.DB
and stuff automatically works. This is also unit test friendly due to all the#if DEBUG
stuff.We do not start any transactions per session, if we did and had updates at the beginning of our session, we would get serious locking issues, as the locks would not be released till the end.
仅当需要时,才可以使用 TransactionScope 当您通过“写入”调用调用数据库时。
请参阅最近问题中的一个随机示例:为什么即使从未调用 TransactionScope.Complete() 也会提交嵌套事务?
您不会为每个http请求打开连接并启动事务。仅按需提供。我很难理解为什么有些人提倡在每个会话中打开一个数据库事务:当您查看数据库事务什么时,这纯粹是白痴。
注意:我本身并不反对这种模式。我反对调用 MSDTC 的不必要的、太长的客户端数据库事务
You'd only start a SQL Server Transaction when you need to with something like TransactionScope when you call the database with a "write" call.
See a random example in this recent question: Why is a nested transaction committed even if TransactionScope.Complete() is never called?
You would not open a connection and start a transaction per http request. Only on demand. I'm having difficulty understanding why some folk advocate opening a database transaction per session: sheer idiocy when you look at what a database transaction is
Note: I'm not against the pattern per se. I am against unnecessary, too long, client-side database transactions that invoke MSDTC