sql-server - 使用 SqlConnection/System.Transactions 的每个请求 session

标签 sql-server sqlconnection system.transactions dapper

我刚刚开始在一个项目中使用 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/

相关文章:

sql-server - Pandas 和 SQLAlchemy : df. to_sql() with SQLAlchemy 2.0 future=True 在使用来自 engine.begin() 的连接时抛出错误

sql-server - SQL Server - 排序规则 - Latin1_General_CI_AS 和 Latin1_General_CS_AS 之间的区别

.net - 跨 AppDomains 和进程的 TransactionScope

architecture - System.Transactions 中避免事务升级的策略

c# - Java 事务 API 和 .NET System.Transactions

sql - 选择两个 IP 范围之间的记录

asp.net - 用于存储表单字段和字段值的数据库架构建议

c# - 打开SQL连接时算术溢出异常

c# - QueryAsync 是否使用 Dapper 调用 OpenAsync?

c# - 我怎样才能真正关闭一个sql连接