首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用SqlConnection / System.Transactions实现每个请求的会话

使用SqlConnection / System.Transactions实现每个请求的会话
EN

Stack Overflow用户
提问于 2011-06-01 21:24:23
回答 2查看 2.1K关注 0票数 15

我刚刚开始在一个项目中使用Dapper,在过去的几年里,我主要使用像NHibernate和EF这样的ORM。

通常,在我们的web应用程序中,我们实现每个请求的会话,在请求开始时开始事务,在结束时提交事务。

在直接使用SqlConnection / System.Transactions时,我们是否应该做类似的事情?

StackOverflow如何做到这一点呢?

解决方案

根据@gbn和@Sam Safron的建议,我没有使用事务。在我的例子中,我只做读查询,所以似乎没有使用事务的真正要求(与我被告知的关于隐式事务的情况相反)。

我创建了一个轻量级会话接口,这样我就可以对每个请求使用一个连接。这对我很有好处,因为对于Dapper,我经常需要创建几个不同的查询来构建对象,并且更愿意共享相同的连接。

确定每个请求的连接范围并处理它的工作由我的IoC容器(StructureMap)完成:

代码语言:javascript
复制
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();
        }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-06-02 08:33:47

这就是我们要做的:

我们在名为Current的对象上定义了名为DB的静态对象

代码语言:javascript
复制
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或其他任何东西。

在我们的应用程序对象上,我们确保连接在请求结束时关闭。

代码语言:javascript
复制
   protected void Application_EndRequest(object sender, EventArgs e)
   {
        Current.DisposeDB(); // closes connection, clears context 
   }

然后,在代码中需要访问数据库的任何地方,只需调用Current.DB,就可以自动工作。由于所有的#if DEBUG内容,这也是单元测试友好的。

我们不会在每个会话中启动任何事务,如果我们在会话开始时启动并更新了任何事务,我们将会遇到严重的锁定问题,因为直到会话结束时才会释放锁。

票数 10
EN

Stack Overflow用户

发布于 2011-06-01 22:13:26

只有在需要时才会启动SQL Server事务,比如在使用"write“调用调用数据库时使用TransactionScope

请看最近这个问题中的一个随机示例:Why is a nested transaction committed even if TransactionScope.Complete() is never called?

您不会为每个http请求打开一个连接并启动一个事务。仅在需要时使用。我很难理解为什么有些人主张每次会话都打开一个数据库事务:当你看到数据库事务是什么时,这简直是愚蠢至极

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6201874

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档