.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};

相关推荐
慧都小妮子11 小时前
Aspose.CAD for .NET 26.6 更新发布
pdf·.net·3d渲染·cad·pdf导出·ifc编辑
EIP低代码平台13 小时前
EIP低代码平台系统-字典功能讲解
低代码·c#·工作流
FuckPatience13 小时前
Telerik UI for WPF 值不能为null。参数名:key
ui·wpf
吉普赛的歌14 小时前
YARP负载均衡配置了多个相同接口导致的报错
c#·yarp
张人玉15 小时前
C# WinForms——工厂管理系统(C# WinForms)
数据库·sqlite·c#·winform
夜莺悠吟17 小时前
关于对 C# 中 ImplicitUsings,GlobalUsings 的讨论
c#·.net
lindexi18 小时前
WPF 笔迹延迟优化从硬件到软件的全链路分析
wpf
-银雾鸢尾-1 天前
C#中的拓展方法
开发语言·c#
EIP低代码平台1 天前
EIP低代码平台-系统参数配置详解|落地三级等保+高灵活私有化部署方案
低代码·c#·权限·工作流·netcore
测试修炼手册2 天前
[测试技术] JUnit 6 入门与实战:断言、参数化与 Mockito
数据库·junit·sqlserver