首页 > 解决方案 > 使用 EF Core 调用存储过程并关闭连接

问题描述

我有一个使用 EF Core 的 ASP.NET Core 2.2 应用程序。我有服务类,它通常DbContext用于任何 CRUD 操作。但是,在其中一种方法(Assign下面的方法)中,我需要使用存储过程。所以我使用下面的代码。请注意,它是作为实例DbContext注入的。Scoped

public class MyService : IMyService
{
   private readonly MyDbContext _dbContext;

   public MyService(MyDbContext dbContext)
   {
      _dbContext = dbContext;
   }

   public async Task<Employee> GetByID(int id)
   {
      return await _dbContext.Employees.FindById(id);
   }

   public async Task<int?> Assign(int itemID, int userID)
   {
        using (var cmd = _dbContext.Database.GetDbConnection().CreateCommand())
        {
            var p1 = new SqlParameter("@ItemId", SqlDbType.Int);
            p1.Value = itemID;

            var p2 = new SqlParameter("@UserID", SqlDbType.Int);
            p2.Value = userID;

            cmd.CommandText = "dbo.prcAssign";
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add(p1);
            cmd.Parameters.Add(p2);

            await _dbContext.Database.OpenConnectionAsync();

            var result = await cmd.ExecuteScalarAsync();

            if (result != null)
            {
                return Convert.ToInt32(result);
            }

            return null;                
        }
    }
}

存储过程在内部使用 SQL 事务。我想知道我是否需要在Assign方法中显式关闭数据库连接,或者连接会在请求端被容器自动关闭?

问题是由于存储过程正在使用事务,它在表上设置了读锁(我们实际上希望表上的读锁以避免获取脏数据)。存储过程需要几毫秒的时间来执行,我们几乎没有 50 个用户。然而,我们经常在Employee桌子上遇到死锁:

System.Data.SqlClient.SqlException (0x80131904):事务(进程 ID 59)与另一个进程在锁资源上死锁,并已被选为死锁受害者。重新运行事务。
在 System.Data.SqlClient.SqlConnection.OnError(SqlException 异常,布尔 breakConnection,操作1 wrapCloseInAction) 在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject> stateObj,布尔调用者HasConnectionLock,布尔 asyncClose) 在 System.Data.SqlClient.TdsParser System.Data.SqlClient.SqlDataReader.TryConsumeMetaData () 处的.TryRun(RunBehavior runBehavior、SqlCommand cmdHandler、SqlDataReader dataStream、BulkCopySimpleResultSet bulkCopyHandler、TdsParserStateObject stateObj、Boolean&dataReady )1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action




在 System.Data.SqlClient.SqlDataReader.get_MetaData()
在 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
在 System.Data.SqlClient.SqlCommand.CompleteAsyncExecuteReader()
在 System.Data.SqlClient .SqlCommand.InternalEndExecuteReader(IAsyncResult asyncResult, String endMethod)
在 System.Data.SqlClient.SqlCommand.EndExecuteReaderInternal(IAsyncResult asyncResult)
在 System.Data.SqlClient.SqlCommand.EndExecuteReader(IAsyncResult asyncResult) > 在 System.Threading.Tasks.TaskFactory 1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction , Action 1 endAction, Task1 promise, Boolean requiresSynchronization)
--- 从先前抛出异常的位置结束堆栈跟踪 ---

在 D:\xxxx\Services\MyService.cs:line 84 中的 xxxxx.Services.MyService.Assign(Int32 itemID, Int32 userID)

标签: c#sql-serverentity-framework-coreef-core-2.0ef-core-2.2

解决方案


我想知道我是否需要在Assign方法中显式关闭数据库连接,或者连接会在请求端被容器自动关闭?

连接是否会在以后自动关闭并不重要。遵循良好的编程实践,应该释放分配的资源(创建 -> 处置、打开 -> 关闭等)。

EF Core 内部正在为每个需要打开连接的操作执行此操作,因此您最好也这样做,例如像这样

await _dbContext.Database.OpenConnectionAsync();
try
{
    var result = await cmd.ExecuteScalarAsync();

    if (result != null)
    {
        return Convert.ToInt32(result);
    }

    return null;
}
finally
{
    _dbContext.Database.CloseConnection();
}

推荐阅读