我刚刚开始在一个项目中使用 Dapper,过去几年主要使用 NHibernate 和 EF 等 ORM。
通常在我们的网络应用程序中,我们为每个请求实现 session ,在请求开始时开始一个事务并在结束时提交它。
直接使用 SqlConnection/System.Transactions 时我们是否应该做类似的事情?
StackOverflow 是如何做到的?
解决方案
听取@gbn 和@Sam Safron 的建议,我没有使用事务。就我而言,我只执行读取查询,因此似乎没有真正要求使用事务(与我被告知的隐式事务相反)。
我创建了一个轻量级 session 接口(interface),以便我可以为每个请求使用一个连接。这对我来说非常有益,因为使用 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();
}
}
}
最佳答案
这就是我们所做的:
我们在名为 Current
的对象上定义一个名为 DB
的静态变量
public static DBContext DB
{
var result = GetContextItem<T>(itemKey);
if (result == null)
{
result = InstantiateDB();
SetContextItem(itemKey, result);
}
return result;
}
public static T GetContextItem<T>(string itemKey, bool strict = true)
{
#if DEBUG // HttpContext is null for unit test calls, which are only done in DEBUG
if (Context == null)
{
var result = CallContext.GetData(itemKey);
return result != null ? (T)result : default(T);
}
else
{
#endif
var ctx = HttpContext.Current;
if (ctx == null)
{
if (strict) throw new InvalidOperationException("GetContextItem without a context");
return default(T);
}
else
{
var result = ctx.Items[itemKey];
return result != null ? (T)result : default(T);
}
#if DEBUG
}
#endif
}
public static void SetContextItem(string itemKey, object item)
{
#if DEBUG // HttpContext is null for unit test calls, which are only done in DEBUG
if (Context == null)
{
CallContext.SetData(itemKey, item);
}
else
{
#endif
HttpContext.Current.Items[itemKey] = item;
#if DEBUG
}
#endif
}
在我们的例子中,InstantiateDB
返回一个 L2S 上下文,但在您的例子中,它可能是一个开放的 SQLConnection
或其他内容。
在我们的应用程序对象上,我们确保连接在请求结束时关闭。
protected void Application_EndRequest(object sender, EventArgs e)
{
Current.DisposeDB(); // closes connection, clears context
}
然后,在代码中需要访问数据库的任何位置,您只需调用 Current.DB
即可自动工作。由于所有 #if DEBUG
内容,这也是单元测试友好的。
我们不会在每个 session 中启动任何事务,如果我们这样做并在 session 开始时进行更新,我们将遇到严重的锁定问题,因为锁直到结束才会被释放。
关于sql-server - 使用 SqlConnection/System.Transactions 的每个请求 session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6201874/