.NET 6 + Dapper + User-Defined Table Type

大家都知道,对于SQL Server IN是有限制条件的,如果IN里面的内容过多,在执行的时候会被自动截断,因而导致查询到的结果不是实际需要的结果。

select * from Payments where Id in (1,2,3,4,...)

为了解决上面的限制,可以改为利用用户自定义数据类型解决。

具体内容如下:

1. Create User-Defined Data Types

sql 复制代码
CREATE TYPE [dbo].[IdTable] AS TABLE(
	[Id] [int] NOT NULL
)
GO

2. Create Store Procedure which use IdTable as Input paramter.

sql 复制代码
CREATE PROCEDURE [dbo].[SP_GET_PAYMENTS]                  
	@PaymentIds [dbo].[IdTable] READONLY                 
AS   
BEGIN  
	select p.Id,p.DueDate   
	from @PaymentIds tp     
	join Payments p with (nolock) on tp.Id=p.Id 
END  

3. Parameter convert to IdTable. SQLMapper Extension

cs 复制代码
  public static class Extensions
  {
      /// <summary>
      /// This extension converts an enumerable set to a Dapper TVP
      /// </summary>
      /// <typeparam name="T">type of enumerbale</typeparam>
      /// <param name="enumerable">list of values</param>
      /// <param name="typeName">database type name</param>
      /// <param name="orderedColumnNames">if more than one column in a TVP, 
      /// columns order must mtach order of columns in TVP</param>
      /// <returns>a custom query parameter</returns>
      public static SqlMapper.ICustomQueryParameter AsTableValuedParameter<T>
          (this IEnumerable<T> enumerable,
          string typeName, IEnumerable<string> orderedColumnNames = null)
      {
          var dataTable = new DataTable();
          if (typeof(T).IsValueType || typeof(T).FullName.Equals("System.String"))
          {
              dataTable.Columns.Add(orderedColumnNames == null ?
                  "NONAME" : orderedColumnNames.First(), typeof(T));
              foreach (T obj in enumerable)
              {
                  dataTable.Rows.Add(obj);
              }
          }
          else
          {
              PropertyInfo[] properties = typeof(T).GetProperties
                  (BindingFlags.Public | BindingFlags.Instance);
              PropertyInfo[] readableProperties = properties.Where
                  (w => w.CanRead).ToArray();
              if (readableProperties.Length > 1 && orderedColumnNames == null)
                  throw new ArgumentException("Ordered list of column names must be provided when TVP contains more than one column");

              var columnNames = (orderedColumnNames ??
                  readableProperties.Select(s => s.Name)).ToArray();
              foreach (string name in columnNames)
              {
                  dataTable.Columns.Add(name, readableProperties.Single
                      (s => s.Name.Equals(name)).PropertyType);
              }

              foreach (T obj in enumerable)
              {
                  dataTable.Rows.Add(
                      columnNames.Select(s => readableProperties.Single
                          (s2 => s2.Name.Equals(s)).GetValue(obj))
                          .ToArray());
              }
          }
          return dataTable.AsTableValuedParameter(typeName);
      }
  }

4. Dapper code

Dapper version: 2.1.37

cs 复制代码
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using Dapper;
using log4net;
using Microsoft.Data.SqlClient;
using static Dapper.SqlMapper;
 
      public List<T> QueryEntitiesNoUTC<T>(string sqlCommand, object parameters, CommandType commandType, int commandTimeout = 60)
       {
           using (SqlConnection Connection = GetSqlConnection(ConfigurationManager.ConnectionStrings["Default"].ConnectionString))
           {
               try
               {
                   Connection.Open();
                   List<T> returnValues = Connection.Query<T>(sqlCommand, parameters, commandType: commandType, commandTimeout: GetCommandTimeout(commandTimeout)).ToList<T>();
                   Connection.Close();
                   Connection.Dispose();
                   return returnValues;
               }
               catch (Exception e)
               {
                   Log.Error(e);
                   throw;
               }
               finally
               {
                   Connection.Close();
                   Connection.Dispose();
               }
           }
       }

5. C# call Demo

cs 复制代码
        public static List<PaymentDto> GetPayment(List<int> paymentIds)
        {
            if (paymentIds == null || paymentIds.Count == 0)
            {
                return new();
            }
            var param = new { PaymentIds = paymentIds.AsTableValuedParameter("dbo.IdTable", new List<string>() { "Id" }) };
            var result = dapperHelper.QueryEntitiesNoUTC<PaymentDto>("SP_GET_PAYMENTS", param, CommandType.StoredProcedure, SqlConstants.GetBiggerRecordsTimeoutSeconds);

            return result;
        }

如果是多个参数:

var param = new { PaymentIds= paymentIds.AsTableValuedParameter("dbo.IdTable", new List<string>() { "Id" }), Status= status};

相关推荐
董先生_ad986ad2 小时前
C# 中的 `lock` 关键字本质
开发语言·c#
爱编程的鱼6 小时前
C# 枚举(Enum)声明与使用详解
java·windows·c#
冰茶_7 小时前
C#中常见的设计模式
java·开发语言·microsoft·设计模式·微软·c#·命令模式
chegan9 小时前
用c#从头写一个AI agent,实现企业内部自然语言数据统计分析(二)-数据结构和代码分析方法
ai·c#·agent
煤烦恼9 小时前
Kafka 命令行操作与 Spark-Streaming 核心编程总结
c#·linq
FAREWELL0007512 小时前
C#进阶学习(十四)反射的概念以及关键类Type
开发语言·学习·c#·反射·type
FAREWELL0007512 小时前
C#进阶学习(十三)C#中的预处理器指令
开发语言·学习·c#·预处理指令
qq_2979080112 小时前
c#简易超市充值卡程序充值消费查余额
经验分享·sqlserver·开源·c#·.net·开源软件