使用SqlConnection / System.Transactions进行会话请求

dapper sqlconnection sql-server 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();
        }
    }
}

一般承认的答案

这就是我们的工作:

我们在名为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东西,这也是单元测试友好的。


我们不会在每个会话中启动任何事务,如果我们在会话开始时进行了更新并且在会话开始时有更新,我们将会遇到严重的锁定问题,因为锁定不会被释放到最后。


热门答案

当你需要的东西,如你只启动一个SQL Server事务TransactionScope的 ,当你调用一个“写”调用数据库。

请参阅最近这个问题中的一个随机示例: 为什么即使从未调用TransactionScope.Complete(),也会提交嵌套事务?

不会打开连接并按http请求启动事务。只有按需。我很难理解为什么有些人提倡每次会话打开一个数据库事务:当你看一下数据库事务是什么时,纯粹是白痴

注意:我不反对模式本身。我反对调用MSDTC的不必要的,太长的客户端数据库事务



许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因