【问题标题】:Why does a dapper query fail and a ado call not?为什么 dapper 查询失败而 ado 调用失败?
【发布时间】:2021-11-02 16:04:01
【问题描述】:

在 netcore 5 C# 中,我在应用程序开始时检查数据库是否在最新版本上,如果不是,则根据特定客户安装的版本,它会自动更新(并针对每个客户执行特定操作)。

我正在重构并试图查看是否可以用 Dapper 调用替换当前的 sql 执行调用,但失败了:

一个。例如,我在字符串中有这段 sql:

SET NUMERIC_ROUNDABORT OFF
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS, NOCOUNT ON SET DATEFORMAT YMD SET XACT_ABORT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION

DECLARE @items AS CURSOR
DECLARE @item AS nvarchar(250)

SET @items = CURSOR FOR
    SELECT
        N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(t.parent_object_id)) +  N'.' + QUOTENAME(OBJECT_NAME(t.parent_object_id)) + '  DROP CONSTRAINT ' +  QUOTENAME(t.name) +  N';'

    FROM
        [sys].[check_constraints] AS t
    WHERE
        t.is_ms_shipped = 0

    OPEN @items FETCH NEXT FROM @items INTO @item WHILE @@FETCH_STATUS = 0

BEGIN
    EXEC (@item)

    FETCH NEXT FROM @items INTO @item

END

CLOSE @items

DEALLOCATE @items 

COMMIT TRANSACTION

b.我用 Dapper 运行这个(在事务中):

public IDbConnection DbConnection
    {
        get
        {
            return new SqlConnection(_connectionstring);
        }
    }

using IDbConnection db = DbConnection;
await db.ExecuteAsync(statement)

并且失败并显示“System.PlatformNotSupportedException:此平台不支持分布式事务。”

c。当我将 dapper 调用替换为:

string connectionString = _context.Database.GetDbConnection().ConnectionString;
SqlConnection _connection = new SqlConnection(connectionString);
await _connection.ExecuteCommandAsync(statement).ConfigureAwait(false);

它运行良好(以及之后的所有其他数百条 sql 语句)

围绕它的交易是:

using (TransactionScope scope
                        = new TransactionScope(TransactionScopeOption.Required, System.TimeSpan.FromMinutes(10),
                                       TransactionScopeAsyncFlowOption.Enabled))
                    {
  // a loop with hundreds of sql files and corresponding calls
  // to execute
}

(我知道https://github.com/dotnet/runtime/issues/19318

根据要求提供完整的堆栈跟踪

System.PlatformNotSupportedException: This platform does not support distributed transactions.
   at System.Transactions.Distributed.DistributedTransactionManager.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
   at System.Transactions.TransactionInterop.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
   at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
   at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
   at System.Transactions.EnlistableStates.Promote(InternalTransaction tx)
   at System.Transactions.Transaction.Promote()
   at System.Transactions.TransactionInterop.ConvertToDistributedTransaction(Transaction transaction)
   at System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)
   at System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()
--- End of stack trace from previous location ---
   at Dapper.SqlMapper.ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, Object param) in /_/Dapper/SqlMapper.Async.cs:line 645

【问题讨论】:

  • 您不需要CURSOR,只需使用STRING_AGG
  • 不应该从属性获取器返回new对象,尤其是像SqlConnection这样的昂贵对象 - 你会泄露本地资源大大地。属性 getter 应该没有副作用。
  • “并且失败并显示“System.PlatformNotSupportedException:此平台不支持分布式事务。” - 请发布该异常的堆栈跟踪。
  • .NET Core 不支持分布式事务(请参阅 stackoverflow.com/questions/56328832/… ) - 我认为您的 ADO 代码有效,因为事务在 SQL 代码中完成 - 尽管我承认我不确定(分布式事务是一个可以'o'蠕虫,imo)
  • 显然工作的 ExecuteCommandAsync:它在哪里定义?这听起来不像普通的 ADO.NET,所以:它是从哪里来的?另请注意:Dapper also 只使用 ADO.NET,所以如果 Dapper 以这种方式失败,它来自 来自 ADO.NET;我很好奇这种其他方法在做什么,因此问题

标签: c# entity-framework dapper asp.net-core-5.0


【解决方案1】:

根据我在评论中的想法,额外的推测(在历史上是“之后”,但在尝试的事情方面是“之前”):完全不改变属性,你可以尝试添加

db.Open();

await db.OpenAsync().ConfigureAwait(false);

到您现有的代码中,假设差异是连接打开的次数。


推测,但我想知道异常是否纯粹是由属性getter每次返回新连接引起的。这意味着您可能涉及多个连接,这(与TransactionScope 结合使用时)可能会导致坏事。也许很简单:

private IDbConnection _db;
public IDbConnection DbConnection
   => _db ?? CreateOpenConnection();
private IDbConnection CreateOpenConnection()
{
    if (_db is null)
    {
        _db = new SqlConnection(_connectionstring);
        _db.Open();
    }
    return _db;
}

【讨论】:

  • 是的,我认为是这样的,尤其是现在我发现它是一个封装了开放逻辑的扩展方法。会试试的。
  • 我不得不多做一点,以确保事务中调用的所有服务都使用相同的类。然后它起作用了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-01
  • 2011-08-08
相关资源
最近更新 更多