我刚刚开始在项目中使用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();
}
}
}
答案 0 :(得分:10)
这就是我们的工作:
我们在名为DB
Current
的静态
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
内容,这也是单元测试友好的。
我们不会在每个会话中启动任何事务,如果我们在会话开始时做了更新,我们就会遇到严重的锁定问题,因为锁不会被释放到最后。
答案 1 :(得分:4)
当您使用“write”调用调用数据库时,只需要使用TransactionScope之类的内容时,就可以启动SQL Server事务。
在最近的问题中查看随机示例:Why is a nested transaction committed even if TransactionScope.Complete() is never called?
您不会打开连接并按http请求启动事务。只在需要时。我很难理解为什么有些人提倡每个会话打开一个数据库事务:当你看到数据库事务的时,纯粹是白痴
注意:我不反对模式本身。我反对调用MSDTC的不必要的,太长的客户端数据库事务