如何避免 ASP.NET SqlServer 会话干扰周围的 Transaction-Per-Request?

发布于 2024-09-02 09:25:17 字数 4539 浏览 5 评论 0原文

我正在尝试在每个请求的事务模式中使用事务范围。所以我有一个 http 模块可以做到(简化):

private void Application_BeginRequest(object sender, EventArgs e)
{
    var scope = new TransactionScope(TransactionScopeOption.RequiresNew);
    PutScopeInHttpContext(scope);
}

private void Application_EndRequest(object sender, EventArgs e)
{
    var scope = GetScopeFromHttpContext();
    try
    {
        if (HttpContext.Current.Error == null)
        {
            scope.Complete();
        }
    }
    finally
    {
        scope.Dispose();
    }
}

然后,在我的 web.config 中,我有:

<httpModules>
    <clear/>
    <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
    <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
    <add name="Profile" type="System.Web.Profile.ProfileModule"/>
    <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add name="TransactionPerRequestWebModule" type="Acme.Web.TransactionPerRequestWebModule, Acme.Web"/>
</httpModules>
<sessionState mode="SQLServer" sqlConnectionString="Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI;" cookieless="false" timeout="360"/>

现在,以看似随机的速度,大约十分之一页给出了以下错误:

[SqlException (0x80131904): Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.]
  System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +1951450
  System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +4849003
  System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +194
  System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +2394
  System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +33
  System.Data.SqlClient.SqlDataReader.get_MetaData() +83
  System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +297
  System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +954
  System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162
  System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
  System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
  System.Data.SqlClient.SqlCommand.ExecuteReader() +89
  System.Web.SessionState.SqlSessionStateStore.DoGet(HttpContext context, String id, Boolean getExclusive, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +516

[HttpException (0x80004005): Unable to connect to SQL Server session database.]
  System.Web.SessionState.SqlSessionStateStore.ThrowSqlConnectionException(SqlConnection conn, Exception e) +229
  System.Web.SessionState.SqlSessionStateStore.DoGet(HttpContext context, String id, Boolean getExclusive, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +649
  System.Web.SessionState.SqlSessionStateStore.GetItemExclusive(HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +48
  System.Web.SessionState.SessionStateModule.GetSessionStateItem() +117
  System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) +487
  System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +66
  System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

我(认为我)我的理解是,与 ASP.NET 会话数据库的连接有时会出现在我的业务事务中,当我的业务事务首先完成时,我会收到此错误。

这有两个问题:

  • 我认为会话状态的事务不应与我的业务事务相同。这是两个不同的问题。
  • 它使事务自动升级为分布式 (MSDTC) 事务,这会影响我的性能。

    如何将我的业务事务与 ASP.NET 会话事务解耦?

    提前致谢,

    Julien

I am trying to use a transaction scope in a transaction-per-request pattern. So I have a http module that do (simplified):

private void Application_BeginRequest(object sender, EventArgs e)
{
    var scope = new TransactionScope(TransactionScopeOption.RequiresNew);
    PutScopeInHttpContext(scope);
}

private void Application_EndRequest(object sender, EventArgs e)
{
    var scope = GetScopeFromHttpContext();
    try
    {
        if (HttpContext.Current.Error == null)
        {
            scope.Complete();
        }
    }
    finally
    {
        scope.Dispose();
    }
}

Then, in my web.config, I have:

<httpModules>
    <clear/>
    <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
    <add name="Session" type="System.Web.SessionState.SessionStateModule"/>
    <add name="Profile" type="System.Web.Profile.ProfileModule"/>
    <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add name="TransactionPerRequestWebModule" type="Acme.Web.TransactionPerRequestWebModule, Acme.Web"/>
</httpModules>
<sessionState mode="SQLServer" sqlConnectionString="Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI;" cookieless="false" timeout="360"/>

Now, at what seem like randomly rate, roughly 1 page out of ten gives me the following error:

[SqlException (0x80131904): Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.]
  System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +1951450
  System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +4849003
  System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +194
  System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +2394
  System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +33
  System.Data.SqlClient.SqlDataReader.get_MetaData() +83
  System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +297
  System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +954
  System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162
  System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
  System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
  System.Data.SqlClient.SqlCommand.ExecuteReader() +89
  System.Web.SessionState.SqlSessionStateStore.DoGet(HttpContext context, String id, Boolean getExclusive, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +516

[HttpException (0x80004005): Unable to connect to SQL Server session database.]
  System.Web.SessionState.SqlSessionStateStore.ThrowSqlConnectionException(SqlConnection conn, Exception e) +229
  System.Web.SessionState.SqlSessionStateStore.DoGet(HttpContext context, String id, Boolean getExclusive, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +649
  System.Web.SessionState.SqlSessionStateStore.GetItemExclusive(HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags) +48
  System.Web.SessionState.SessionStateModule.GetSessionStateItem() +117
  System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData) +487
  System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +66
  System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

What I (think I) understand is that the connection to the ASP.NET session database is sometimes enlisted in my business transaction, and I get this error when my business transaction is completing first.

There is a couple of problem with that:

  • I don't think the transaction for the session state should be the same as my business one. These are 2 separate concerns.
  • It makes the transaction automatically escalate to a distributed (MSDTC) one, which impacts my performance.

    How do I decouple my business transaction from the ASP.NET session one?

    Thanks in advance,

    Julien

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

仅一夜美梦 2024-09-09 09:25:17

这是一个死胡同,但万一其他人在谷歌搜索时随机遇到这个问题,问题的答案是将 Enlist=False 添加到 SqlServerSessionState 连接字符串

由于奇怪的时间安排,用于检索会话的连接有时会加入环境事务中,即使您可能不希望这样做。

在连接字符串上设置 Enlist=False 可确保您的会话状态连接不会获取环境事务,从而导致快速事务提升为较慢的分布式事务。

This is a Necro-Answering, but the answer to the question in case anyone else comes across this randomly while Googling is to add Enlist=False to the SqlServerSessionState connection string.

Due to odd timing, the connection used to retrieve session can sometimes enlist in an ambient transaction, even though you probably would prefer that it wouldn't.

Setting Enlist=False on the connection string ensures that your session state connections won't pick up the ambient transaction and thus cause a nice fast transaction to get promoted to a slower distributed transaction.

小女人ら 2024-09-09 09:25:17

我不完全确定您的代码是如何工作的,但是您可以像这样抑制环境事务,这可能会有所帮助:

using(TransactionScope scope1 = new TransactionScope())
{
     try
     {
          //Start of non-transactional section 
          using(TransactionScope scope2 = new
             TransactionScope(TransactionScopeOption.Suppress))
          {
               //Do non-transactional work here
          }
          //Restores ambient transaction here
   }
     catch
     {}
   //Rest of scope1
}

I'm not entirely sure of how your code is working, but you can suppress the ambient transaction like this, which may help:

using(TransactionScope scope1 = new TransactionScope())
{
     try
     {
          //Start of non-transactional section 
          using(TransactionScope scope2 = new
             TransactionScope(TransactionScopeOption.Suppress))
          {
               //Do non-transactional work here
          }
          //Restores ambient transaction here
   }
     catch
     {}
   //Rest of scope1
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文