【问题标题】:Dapper.NET Connection/Query Best PracticeDapper.NET 连接/查询最佳实践
【发布时间】:2015-07-21 06:16:37
【问题描述】:

所以我已经阅读了一堆链接/SO问题,但我仍然无法得到明确的答案。

在 ASP.NET 应用程序中使用 Dapper 执行 SQL 查询时,打开/关闭连接的最佳做法是什么?

这是我目前遵循的模式:

using (var db = new SqlConnection(_connectionString))
{
    return db.Query("dbo.SomeQuery");
}

基本上,每次都根据需要打开/关闭 SQL 连接。

据我了解,上述代码应该会自动打开/关闭 SQL 连接(例如,我不需要明确地执行 db.Opendb.Close)。

我看到的问题是,一段时间后,我收到了一堆这样的错误:

InvalidOperationExceptionTimeout 已过期。超时时间已过 在从池中获取连接之前。这可能已经发生 因为所有池连接都在使用中并且最大池大小是 到达。

在此期间我有一个 SQL 探查器跟踪,我没有看到任何会阻止任何内容的长查询,所以看起来我的 ASP.NET Web 应用程序连接不足(而不是执行查询的时间过长)。

谁能告诉我我做错了什么?

附注:我的应用程序作为 Azure Web 应用程序运行,所以很遗憾,我实际上无法看到 Web 应用程序打开了多少连接。 :(

【问题讨论】:

标签: c# asp.net .net sql-server dapper


【解决方案1】:

这是一个在内部使用 Dapper 的数据库上下文示例,我认为这些是很好的最佳实践。我把它放在一个项目中,它有演变、来自不同地方的最佳想法以及我自己的一些经验/输入。此上下文也示例了不同类型的数据库操作:

  • 标量
  • 列表
  • 单一实体
  • 删除
  • 更新
  • 动态查询构建
  • 获取新插入的记录 ID
  • 使用 SqlMapper.GridReader 处理多个结果集

ASP.NET Core Options 模式还用于注入设置/连接字符串信息,与 Dapper 无关,但我认为由于 Dapper 在 .NET 应用程序中大量使用,因此仍然很方便。

我引入了一种代码风格概念,即调用包含原始数据“实体”的类,并将为调用者清理的转换数据称为“模型”。 “ViewModel”专门用于我堆栈中的前端。有时 Context 会向调用者返回一个模型,有时会返回一个原始实体,因为调用者需要原始实体进行比 Context 应该负责的更深入的转换。这个概念可能并不完美,在某些方面我是自己编造的,因为我发现“模型”这个词在不同的堆栈、团队和公司中有很多含义。我们为世界建模,数据库可以被称为模型,实体可以被认为是模型......无论如何,它是一门艺术和一门科学就是我所说的,只是想在下面的代码中更好地解释返回类型. :)

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
using Microsoft.Extensions.Options;
using Tsl.CustomPrice.Interfaces;
using Tsl.CustomPrice.Model;
using Tsl.CustomPrice.Model.Configuration;
using Tsl.CustomPrice.Model.Tso;
using Tsl.Shared.Enumeration;

namespace Tsl.CustomPrice.Data
{
    public class TsoContext : ITsoContext
    {
        private readonly string _connectionString;
        private IDbConnection Connection => new SqlConnection(_connectionString);

        public TsoContext(IOptions<DbSettings> settings)
        {
            _connectionString = settings.Value.ConnectionStrings.TsoConnection;
        }

        #region Custom Price Column

        public int GetCustomPriceColumnCountForUser(int userId)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"SELECT count(*)
                            FROM [TSO].[dbo].[CustomPriceColumn] (NOLOCK)
                            WHERE [EntityID] = @userId and [EntityTypeID] = 1 --User";

                return conn.ExecuteScalar<int>(query, new { userId });
            }
        }

        public CustomPriceColumnModel GetLastUpdatedCustomPriceColumn(int userId)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"SELECT [CustomPriceColumnID]
                              ,[EntityID]
                          FROM [TSO].[dbo].[CustomPriceColumn] (NOLOCK)
                            WHERE [EntityID] = @userId and [EntityTypeID] = 1 --User
                            ORDER BY [LastUpdatedDateTime] desc";

                return conn.Query<CustomPriceColumnModel>(query, new { userId }).FirstOrDefault();
            }
        }

        public CustomPriceColumnModel GetCustomPriceColumn(int customPriceColumnId, int userId)
        {
            using (IDbConnection conn = Connection)
            {
                const string query = @"SELECT [CustomPriceColumnID]
                          ,[EntityID]
                          ,[EntityTypeID]
                          ,[CustomPriceColumnTypeID]
                          ,a.[CreatedDateTime]
                          ,case when (CreatedByUserID = @userId or CustomPriceColumnTypeID = 2) then 1 else 0 end as IsEditable
                          ,b.FirstName as CreatedByFirstName
                          ,b.LastName as CreatedByLastName
                      FROM [dbo].[CustomPriceColumn] a (nolock)
                      left join [User] b on b.UserID = a.CreatedByUserID
                      WHERE [CustomPriceColumnID] = @customPriceColumnId";

                return conn.QueryFirstOrDefault<CustomPriceColumnModel>(query, new { @customPriceColumnId=customPriceColumnId, @userId=userId });
            }
        }
        public IEnumerable<CustomPriceColumnModel> GetCustomPriceColumns(int userId)
        {
            using (IDbConnection conn = Connection)
            {
                const string query = @"SELECT
                              [CustomPriceColumnID]
                              ,[EntityID]
                              ,[EntityTypeID]
                              ,case when (CreatedByUserID = @userId or CustomPriceColumnTypeID = 2) then 1 else 0 end as IsEditable
                              ,b.FirstName as CreatedByFirstName
                              ,b.LastName as CreatedByLastName
                            FROM CustomPriceColumn cpc (nolock)
                                inner join [User] u (nolock)
                                    on u.UserID = @userId
                                left join [User] b on b.UserID = CreatedByUserID
                            WHERE (EntityID = @userId and EntityTypeID = 1)
                                or (CreatedByUserID = @userId)
                                or (EntityID = u.CompanyID and EntityTypeID = 0)";

                return conn.Query<CustomPriceColumnModel>(query, new { userId });
            }
        }


        public int CreateCustomPriceColumn(string customPriceColumnName, string customPriceColumnDescription, int entityId, int createdByUserId, string countryCode, IndustryTypes industryTypeId, EntityTypes entityTypeId, CustomPriceColumnTypes customPriceColumnTypeId, string systemUserName, string actorName)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"INSERT INTO [TSO].[dbo].[CustomPriceColumn]
                                       ([EntityID]
                                       ,[EntityTypeID]
                                       ,[CustomPriceColumnTypeID]
                                       ,[CreatedByUserID]
                                       ,[IndustryTypeID]
                                       ,[CountryCode]
                                       ,[CustomPriceColumnName]
                                       ,[CustomPriceColumnDescription]
                                       ,[CreatedDateTime]
                                       ,[LastUpdatedDateTime]
                                       ,[ActorName]
                                       ,[SystemUserName])
                                 VALUES
                                       (@entityId
                                       ,@entityTypeId
                                       ,@customPriceColumnTypeId
                                       ,@createdByUserId
                                       ,@industryTypeId
                                       ,@countryCode
                                       ,@customPriceColumnName
                                       ,@customPriceColumnDescription
                                       ,getdate()
                                       ,getdate()
                                       ,@actorName
                                       ,@systemUserName);
                                    SELECT CAST(SCOPE_IDENTITY() as int)";


                return conn.ExecuteScalar<int>(query,
                    new
                    {
                        entityId,
                        entityTypeId,
                        customPriceColumnTypeId,
                        createdByUserId,
                        industryTypeId,
                        countryCode,
                        customPriceColumnName,
                        customPriceColumnDescription,
                        actorName,
                        systemUserName
                    });

            }
        }

        public void UpdateCustomPriceColumn(int customPriceColumnId, string customPriceColumnName, string customPriceColumnDescription, int entityId, IndustryTypes industryTypeId, EntityTypes entityTypeId, CustomPriceColumnTypes customPriceColumnTypeId, string systemUserName, string actorName)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"UPDATE [TSO].[dbo].[CustomPriceColumn]
                           SET [EntityID] = @entityId
                              ,[EntityTypeID] = @entityTypeId
                              ,[CustomPriceColumnTypeID] = @customPriceColumnTypeId
                              ,[IndustryTypeID] = @industryTypeId
                              ,[CustomPriceColumnName] = @customPriceColumnName
                              ,[CustomPriceColumnDescription] = @customPriceColumnDescription
                              ,[LastUpdatedDateTime] = getdate()
                         WHERE [CustomPriceColumnID] = @customPriceColumnId";


                conn.Execute(query,
                    new
                    {
                        customPriceColumnId,
                        entityId,
                        entityTypeId,
                        customPriceColumnTypeId,
                        industryTypeId,
                        customPriceColumnName,
                        customPriceColumnDescription,
                        actorName,
                        systemUserName
                    });

            }
        }

        public void DeleteCustomPriceColumn(int customPriceColumnId)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"DELETE FROM [TSO].[dbo].[CustomPriceColumn]
                             WHERE [CustomPriceColumnID] = @customPriceColumnId";


                conn.Execute(query,
                    new
                    {
                        customPriceColumnId
                    });

            }
        }

        public CustomPriceColumnMetaDataForCpfExportEntity GetCustomPriceColumnMetaDataForCpfExport(int customPriceColumnId)
        {
            var ret = new CustomPriceColumnMetaDataForCpfExportEntity();
            using (IDbConnection conn = Connection)
            {
                const string query = @"
                    -- TOTAL RULES VS. TOTAL PERCENT RULES
                    SELECT tr.TotalRules, trp.TotalPercentRules FROM
                    (SELECT CustomPriceColumnId, COUNT(*) AS TotalRules FROM tso.dbo.CustomPriceRule WHERE CustomPriceColumnID = @CustomPriceColumnId GROUP BY CustomPriceColumnID) as tr
                    JOIN
                    (SELECT CustomPriceColumnId, COUNT(*) AS TotalPercentRules FROM tso.dbo.CustomPriceRule WHERE CustomPriceColumnID = @CustomPriceColumnId AND IsPercent = 1 GROUP BY CustomPriceColumnID) AS trp
                    ON tr.CustomPriceColumnID = trp.CustomPriceColumnID;
                    -- TOTAL RULES BY BASE COLUMN
                    SELECT BaseColumnPriceTypeID, OperationTypeId, COUNT(*) AS TotalRules FROM tso.dbo.CustomPriceRule WHERE CustomPriceColumnID = @CustomPriceColumnId
                    GROUP BY BaseColumnPriceTypeID, OperationTypeId";

                using (SqlMapper.GridReader multi = conn.QueryMultiple(query, new { @customPriceColumnId = customPriceColumnId }))
                {
                    ret.MetaData = multi.Read<CustomPriceColumnMetaDataEntity>().SingleOrDefault();
                    ret.BasePriceColumnRuleCounts = multi.Read<BasePriceColumnRuleCountEntity>().ToList();
                }

                return ret;
            }
        }
        #endregion

        #region Custom Price Rule

        public IEnumerable<int> GetCustomPriceRulesIds(int customPriceColumnId)
        {

            using (IDbConnection conn = Connection)
            {
                var query =
                    @"SELECT [CustomPriceRuleId] FROM [dbo].[CustomPriceRule] (nolock) WHERE [CustomPriceColumnId] = @customPriceColumnId";

                return conn.Query<int>(query, new {customPriceColumnId});
            }

        }

        public IEnumerable<CustomPriceRuleModel> GetCustomPriceRules(int customPriceColumnId, int index, int pageSize)
        {
            //implementation can be extended to allow sorting by other 
            var sortBy = "a.CreatedDateTime desc";

            using (IDbConnection conn = Connection)
            {
                var query = @"SELECT  *
                            FROM     
                                (SELECT ROW_NUMBER() OVER ( ORDER BY {0}) AS RowNum,  
                                    COUNT(*) OVER () AS TotalRows, 
                                    [CustomPriceRuleId] 
                                    FROM [dbo].[CustomPriceRule] a (nolock) 
                                        left outer join [dbo].[Commodity] b (nolock) on a.CommodityId = b.CommodityID 
                                        left outer join [dbo].[Company] c (nolock) on a.ManufacturerCompanyId = c.CompanyId 
                                        left outer join [dbo].[Item] d (nolock) on a.ItemId = d.ItemID 
                                    WHERE [CustomPriceColumnId] = @customPriceColumnId 
                                  ) AS result 
                            WHERE RowNum BETWEEN ( ((@index - 1) * @pageSize )+ 1) AND @index*@pageSize 
                                        ORDER BY RowNum";

                query = string.Format(query, sortBy);

                return conn.Query<CustomPriceRuleModel>(query, new { customPriceColumnId, index, pageSize });
            }
        }

        public CustomPriceRuleModel GetCustomPriceRule(int customPriceRuleId)
        {
            using (IDbConnection conn = Connection)
            {
                const string query = @"SELECT [CustomPriceRuleId]
                                      ,[CustomPriceColumnId]
                                  FROM [TSO].[dbo].[CustomPriceRule]
                                  WHERE [CustomPriceRuleId] = @customPriceRuleId";

                return conn.QueryFirstOrDefault<CustomPriceRuleModel>(query, new { customPriceRuleId });
            }
        }

        public CustomPriceRuleModel GetCustomPriceRuleByItemId(int customPriceColumnId, int itemId)
        {
            using (IDbConnection conn = Connection)
            {
                const string query = @"SELECT [CustomPriceRuleId]
                                      ,[CustomPriceColumnId]
                                      ,[CustomPriceRuleLevelId]
                                  FROM [TSO].[dbo].[CustomPriceRule]
                                  WHERE [CustomPriceColumnId] = @customPriceColumnId and [ItemId] = @itemId";

                return conn.QueryFirstOrDefault<CustomPriceRuleModel>(query, new { customPriceColumnId, itemId });
            }
        }

        public CustomPriceRuleModel FindCustomPriceRule(int customPriceColumnId, CustomPriceRuleLevels customPriceRuleLevel,
                int? itemId, int? manufacturerCompanyId, int? commodityId, string ucc)
        {
            using (IDbConnection conn = Connection)
            {
                string query = @"SELECT [CustomPriceRuleId]
                                      ,[CustomPriceColumnId]
                                      ,[UCC]
                                  FROM [TSO].[dbo].[CustomPriceRule]
                                  WHERE [CustomPriceColumnId] = @customPriceColumnId
                                  AND [CustomPriceRuleLevelId] = @customPriceRuleLevel";
                var parameters = new DynamicParameters();
                parameters.Add("@customPriceColumnId", customPriceColumnId);
                parameters.Add("@customPriceRuleLevel", (int)customPriceRuleLevel);

                switch (customPriceRuleLevel)
                {
                    case (CustomPriceRuleLevels.Item):
                        query += @" AND ItemId = @itemId";
                        parameters.Add("@itemId", itemId);
                        break;
                    case (CustomPriceRuleLevels.ManufacturerAndCommodity):
                        query += @" AND ManufacturerCompanyID = @manufacturerCompanyId
                            AND CommodityId = @commodityId";
                        parameters.Add("@manufacturerCompanyId", manufacturerCompanyId);
                        parameters.Add("@commodityId", commodityId);
                       break;
                    case (CustomPriceRuleLevels.Manufacturer):
                        query += @" AND ManufacturerCompanyID = @manufacturerCompanyId";
                        parameters.Add("@manufacturerCompanyId", manufacturerCompanyId);
                        break;
                    case (CustomPriceRuleLevels.Commodity):
                        query += @" AND CommodityId = @commodityId";
                        parameters.Add("@commodityId", commodityId);
                        break;
                    case (CustomPriceRuleLevels.Ucc):
                        query += @" AND ManufacturerCompanyID = @manufacturerCompanyId
                            AND Ucc = @ucc";
                        parameters.Add("@manufacturerCompanyId", manufacturerCompanyId);
                        parameters.Add("@ucc", ucc);
                        break;
                }

                return conn.QueryFirstOrDefault<CustomPriceRuleModel>(query, parameters);
            }
        }

        public void UpdateCustomPriceRule(int customPriceRuleId, CustomPriceRuleLevels customPriceRuleLevel, int? itemId, int? manufactuerCompanyId,
            int? commodityId, PriceTypes? baseColumnPriceTypeId, CustomPriceOperations? operationTypeId, decimal customPriceRuleValue, bool isPercent, string customPriceRuleDescription,
            Uom? fixedPriceUnitIfMeasureTypeCode, string ucc, string actorName, string systemUsername)
        {

            using (IDbConnection conn = Connection)
            {
                var query = @"UPDATE [TSO].[dbo].[CustomPriceRule]
                               SET [CustomPriceRuleLevelId] = @customPriceRuleLevel
                                  ,[ItemId] = @itemId
                                  ,[ManufacturerCompanyId] = @manufactuerCompanyId
                                  ,[CommodityId] = @commodityId
                                  ,[BaseColumnPriceTypeId] = @baseColumnPriceTypeId
                                  ,[OperationTypeId] = @operationTypeId
                                  ,[CustomPriceRuleValue] = @customPriceRuleValue
                                  ,[IsPercent] = @isPercent
                                  ,[CustomPriceRuleDescription] = @customPriceRuleDescription
                                  ,[FixedPriceUnitOfMeasureTypeCode] = @strUom
                                  ,[LastUpdatedDateTime] = getdate()
                                  ,[ActorName] = @actorName
                                  ,[SystemUsername] = @systemUsername
                                  ,[UCC] = @ucc
                             WHERE [CustomPriceRuleId] = @customPriceRuleId";

                var strUom = fixedPriceUnitIfMeasureTypeCode != null ? fixedPriceUnitIfMeasureTypeCode.ToString() : null;
                // HACK: See TSL-1235 : CustomPriceOperations.FixedPrice must translate to a null in the CustomPriceRule row.
                CustomPriceOperations? opTypeId = operationTypeId == CustomPriceOperations.FixedPrice ? null : operationTypeId;

                conn.Execute(query,
                    new
                    {
                        customPriceRuleId,
                        customPriceRuleLevel,
                        itemId,
                        manufactuerCompanyId,
                        commodityId,
                        baseColumnPriceTypeId,
                        operationTypeId = opTypeId,
                        customPriceRuleValue,
                        isPercent,
                        customPriceRuleDescription,
                        strUom,
                        ucc,
                        actorName,
                        systemUsername
                    });

            }
        }



        public int CreateCustomPriceRule(int customPriceColumnId, CustomPriceRuleLevels customPriceRuleLevel, int? itemId,
        int? manufactuerCompanyId, int? commodityId, PriceTypes? baseColumnPriceTypeId, CustomPriceOperations? operationTypeId,
        decimal customPriceRuleValue, bool isPercent, string customPriceRuleDescription, Uom? fixedPriceUnitIfMeasureTypeCode,
        string ucc, string actorName, string systemUsername)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"INSERT INTO [TSO].[dbo].[CustomPriceRule]
                               ([CustomPriceColumnId]
                               ,[CustomPriceRuleLevelId]
                               ,[ItemId]
                               ,[ManufacturerCompanyId]
                               ,[CommodityId]
                               ,[BaseColumnPriceTypeId]
                               ,[OperationTypeId]
                               ,[CustomPriceRuleValue]
                               ,[IsPercent]
                               ,[CustomPriceRuleDescription]
                               ,[FixedPriceUnitOfMeasureTypeCode]
                               ,[CreatedDateTime]
                               ,[LastUpdatedDateTime]
                               ,[ActorName]
                               ,[SystemUsername]
                               ,[UCC])
                         VALUES
                               (@customPriceColumnId
                               ,@customPriceRuleLevel
                               ,@itemId
                               ,@manufactuerCompanyId
                               ,@commodityId
                               ,@baseColumnPriceTypeId
                               ,@operationTypeId
                               ,@customPriceRuleValue
                               ,@isPercent
                               ,@customPriceRuleDescription
                               ,@strUom
                               ,getdate()
                               ,getdate()
                               ,@actorName
                               ,@systemUsername
                               ,@ucc);
                                    SELECT CAST(SCOPE_IDENTITY() as int)";

                var strUom = fixedPriceUnitIfMeasureTypeCode != null ? fixedPriceUnitIfMeasureTypeCode.ToString() : null;

                return conn.ExecuteScalar<int>(query,
                    new
                    {
                        customPriceColumnId,
                        customPriceRuleLevel,
                        itemId,
                        manufactuerCompanyId,
                        commodityId,
                        baseColumnPriceTypeId,
                        operationTypeId,
                        customPriceRuleValue,
                        isPercent,
                        customPriceRuleDescription,
                        strUom,
                        ucc,
                        actorName,
                        systemUsername
                    });

            }
        }

        public void DeleteCustomPriceRule(int customPriceRuleId)
        {
            using (IDbConnection conn = Connection)
            {
                var query = @"DELETE FROM [TSO].[dbo].[CustomPriceRule]
                             WHERE [CustomPriceRuleId] = @customPriceRuleId";


                conn.Execute(query,
                    new
                    {
                        customPriceRuleId
                    });

            }
        }

        public void DeleteCustomPriceRules(IEnumerable<int> customPriceRuleIds)
        {
            var cprIdsList = customPriceRuleIds.ToList();

            if (!cprIdsList.Any()) return;

            using (IDbConnection conn = Connection)
            {
                var query = @"DELETE FROM [TSO].[dbo].[CustomPriceRule]
                             WHERE [CustomPriceRuleId] in ("
                            + string.Join(",", cprIdsList)
                            + ")";


                conn.Execute(query);

            }
        }

        public List<CustomPriceRuleForExportEntity> GetCustomPriceRulesForExport(int customPriceColumnId)
        {
            using (IDbConnection conn = Connection)
            {
                const string query = @"SELECT 
                    cpr.CustomPriceRuleLevelID
                    ,cpr.Ucc
                    ,i.Upc
                    ,c.CommodityCode
                    ,mu.ShortName as ManufacturerShortName
                    ,i.ManufacturerCatalogCode
                    ,cpr.CustomPriceRuleDescription
                    ,cpr.BaseColumnPriceTypeId
                    ,cpr.OperationTypeId
                    ,cpr.CustomPriceRuleValue
                    ,cpr.IsPercent
                    ,cpr.ItemId
                    ,cpr.ManufacturerCompanyId
                    ,cpr.CommodityId
                    FROM TSO.dbo.CustomPriceRule cpr
                    LEFT OUTER JOIN TSO.dbo.Item i ON cpr.ItemId = i.ItemId
                    LEFT OUTER JOIN TSO.dbo.ManufacturerUcc mu
                        ON ((cpr.CustomPriceRuleLevelId <> 1 AND cpr.ManufacturerCompanyId = mu.CompanyID AND cpr.UCC = mu.UCC)
                        OR (cpr.CustomPriceRuleLevelId = 1 AND LEFT(i.UPC, 6) = mu.UCC) and i.ManufacturerCompanyID = mu.CompanyID)
                    LEFT OUTER JOIN TSO.dbo.Commodity c ON cpr.CommodityId = c.CommodityId
                    WHERE cpr.CustomPriceColumnId = @customPriceColumnId";

                return conn.Query<CustomPriceRuleForExportEntity>(query, new { @customPriceColumnId = customPriceColumnId }).ToList();
            }
        }

        #endregion

        public bool IsAllowedToModifyCustomPriceColumn(int userId, int customPriceColumnId)
        {
            using (IDbConnection conn = Connection)
            {
                // Check access to CP column.
                var getCpQuery = @"SELECT [CustomPriceColumnID]
                FROM [CustomPriceColumn] cpc
                JOIN [User] u ON u.UserId = @userId
                WHERE cpc.[CustomPriceColumnId] = @customPriceColumnId
                AND ((cpc.[CreatedByUserID] = @userId) /* Created by the User */
                OR (cpc.EntityID = u.CompanyId and cpc.EntityTypeID = 0 AND CustomPriceColumnTypeID = 2)) /* OR CREATED BY SOMEONE IN THE COMPANY AND MARKED PUBLIC-EDITABLE */"; 
                return conn.Query<CustomPriceColumnModel>(getCpQuery, new { @customPriceColumnId = customPriceColumnId, @userId = userId }).SingleOrDefault() != null;

            }
        }
    }
}

【讨论】:

    【解决方案2】:

    根据我对这种非常相似的体验的经验,超时可能是由于 SQL 连接超时。这可能是因为,假设您的 SQL 服务器在 X 核心服务器上运行,并且所有可能的最大连接数都被并发调用使用,随后的调用进入等待模式。如果未指定,它将采用默认连接超时。

    您可能希望在连接字符串或数据仓库层中覆盖它。检查 ConnectionTimeout 道具。

    【讨论】:

      【解决方案3】:

      在我看来,您的代码很好,Dapper 将自行打开和关闭集合,但您得到的异常是因为连接池在超时时间没有空闲连接可以返回。其原因可能是:

      1. 您的查询是长期运行的
      2. 您的连接池大小对于您的用户负载来说很小 有(默认为 100)
      3. 1 和 2 的混合

      因此,请根据您的需要调整连接池大小并调整您的查询和数据库本身。或者使用更大的连接超时。

      PS:手动关闭连接时,Connection Pool实际上并没有关闭它,因为打开和关闭与数据库的连接的成本很高,所以让Dapper打开是没有问题的并自行关闭连接。

      【讨论】:

        【解决方案4】:

        我遇到了你的问题。我在我的工作项目中使用了 dapper。我也遇到过同样的问题。

        查找打开的连接

        首先你应该对sql server执行“sp_who2”来打开sql连接

        此代码返回:总连接大小、数据库名称、阻塞者和状态

        例如:

        这张图片是在 Azure Sql 数据库中拍摄的

        SELECT   s.session_id, 
                r.status, 
                r.blocking_session_id                                 'Blk by', 
                r.wait_type, 
                wait_resource, 
                r.wait_time / (1000.0)                             'Wait Sec', 
                r.cpu_time, 
                r.logical_reads, 
                r.reads, 
                r.writes, 
                r.total_elapsed_time / (1000.0)                    'Elaps Sec', 
                Substring(st.TEXT,(r.statement_start_offset / 2) + 1, 
                        ((CASE r.statement_end_offset 
                            WHEN -1 
                            THEN Datalength(st.TEXT) 
                            ELSE r.statement_end_offset 
                            END - r.statement_start_offset) / 2) + 1) AS statement_text, 
                Coalesce(Quotename(Db_name(st.dbid)) + N'.' + Quotename(Object_schema_name(st.objectid,st.dbid)) + N'.' + Quotename(Object_name(st.objectid,st.dbid)), 
                        '') AS command_text, 
                r.command, 
                s.login_name, 
                s.host_name, 
                s.program_name, 
                s.last_request_end_time, 
                s.login_time, 
                r.open_transaction_count 
                FROM     sys.dm_exec_sessions AS s 
                JOIN sys.dm_exec_requests AS r 
                ON r.session_id = s.session_id 
                CROSS APPLY sys.Dm_exec_sql_text(r.sql_handle) AS st 
                WHERE    r.session_id = 122
                ORDER BY r.cpu_time desc, r.status, 
                r.blocking_session_id, 
                s.session_id 
        

        输入会话 ID 以查找查询

        更改查询方式

        public T WithConnection<T>(Func<IDbConnection, T> dbOperation)
            {
                try
                {
                    using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DatabaseName"].ConnectionString))
                    {
                        var result = dbOperation(connection);
                        connection.Close();
                        return result;
                    }
                }
                catch (TimeoutException ex)
                {
                    throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
                }
                catch (SqlException ex)
                {
                    throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
                }
                catch (InvalidOperationException ex)
                {
                    throw new Exception(String.Format("{0}.WithConnection() experienced a InvalidOperationException", GetType().FullName), ex);
                }
            }
        

        如何使用

        return WithConnection(c =>
                {
                    return c.Query<int>(sql,new { actId=actId}).FirstOrDefault();
                });
        

        这解决了我的问题

        【讨论】:

        • 你为什么在这里使用connection.Close()? “使用”可以解决这个问题。
        【解决方案5】:

        我为 dapper 定义了一个通用包装器 :)

         class DapperWrapper<T>
        {        
            private static string connectionString = "your connection string";
        
            public static List<T> Get_List(string query, DynamicParameters args = null)
            {
                var watch = System.Diagnostics.Stopwatch.StartNew();
        
                List<T> result = new List<T>();
        
                try
                {
                    using (var connection = new SqlConnection(connectionString))
                    {
                        connection.Open();
        
                        result = connection.Query<T>(query, args).ToList();
        
                        connection.Close();
                    }
        
                    return result;
                }
                catch (Exception ex)
                {
                    SqlException(query, ex.Message, watch.ElapsedMilliseconds);
                }
                finally
                {
                    watch.Stop();
                }
        
                return null;
            }
        
            public static T Get_Single(string query, DynamicParameters args = null)
            {
                T result;
        
                var watch = System.Diagnostics.Stopwatch.StartNew();
        
                try
                {
                    using (var connection = new SqlConnection(connectionString))
                    {
                        connection.Open();
        
                        result = connection.Query<T>(query, args).FirstOrDefault();
        
                        connection.Close();
                    }
        
                    return result;
                }
                catch (Exception ex)
                {
                    SqlException(query, ex.Message, watch.ElapsedMilliseconds);
                }
                finally
                {
                    watch.Stop();
                }
        
                return default(T);
            }
        
            public static bool Execute(string query, DynamicParameters args = null, Log logSuccess = null)
            {
                var watch = System.Diagnostics.Stopwatch.StartNew();
        
                try
                {
                    using (var connection = new SqlConnection(connectionString))
                    {
                        connection.Open();
        
                        var isSuccess = connection.Execute(query, args)> 0;
        
                        if (isSuccess && logSuccess != null)
                            Logs.Add(logSuccess);
        
                        connection.Close();
        
                        return isSuccess;
                    }
                }
                catch (Exception ex)
                {
                    SqlException(query, ex.Message, watch.ElapsedMilliseconds);
                }
                finally
                {                
                    watch.Stop();
                }
        
                return false;
            }
        
            private static void SqlException(string query, string exception, long elapsedMilliseconds)
            {
                var error = $"query : {query} → exception : {exception} → time : {elapsedMilliseconds}";
        
                Logs.Add(
                    new Log()
                    {
                        Title = "Error in dapper",
                        Description = error
                    }
                );
            }
        }
        

        您可以使用以下示例

        #------------Get list
        IList<Post> posts = DapperWrapper<Post>.Get_List("SELECT * FROM posts");
        
        #------------Get single
        Post post = DapperWrapper<Post>.Get_Single("SELECT * FROM posts where id=1");
        
        #------------Excute
        bool isSuccess = DapperWrapper<Post>.Execute("DELETE posts where id=1");
        
        #------------Excute with parameter
        SqlParameter[] parameters = {
                                new SqlParameter("@Id",1)
        };
        var args = new DynamicParameters(new { });    
        parameters.ToList().ForEach(p => args.Add(p.ParameterName, p.Value));
        
        bool isSuccess = DapperWrapper<Post>.Execute("DELETE posts where id=@Id", args);
        

        【讨论】:

        • 为什么需要在 using 语句中显式打开和关闭连接? using 将为您处理,这是使用 using 的主要原因
        【解决方案6】:

        using block 将处理资源管理,因此您无需手动关闭连接,而且手动关闭连接会阻止连接池并降低性能。

        【讨论】:

          【解决方案7】:

          从我的观点来看,使用语句关闭你的连接,但你明确地告诉你打开一个像这样的连接:

          using (var db = new SqlConnection(_connectionString))
          
          {
                 db.open();
          
              return db.Query("dbo.SomeQuery");
          }

          【讨论】:

          • Dapper 使用if (wasClosed) cnn.Open();
          • 感谢您的回答,但问题更多在于连接关闭/池。连接正在打开很好。
          猜你喜欢
          • 1970-01-01
          • 2015-03-25
          • 2013-05-10
          • 1970-01-01
          • 2015-08-03
          • 2012-10-10
          • 2022-07-08
          • 2013-10-02
          • 2013-01-06
          相关资源
          最近更新 更多