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

相关推荐
暖馒8 小时前
Modbus应用层协议的深度剖析
网络·网络协议·c#·wpf·智能硬件
R1nG86310 小时前
HCCL vs NCCL代码级对比 hccl/algorithms/ vs nccl/src/collectives/ Ring算法实现差异
wpf·cann
刘欣的博客12 小时前
C# CS架构程序发版升级的走数据库方案
c#·单文件升级自己的方式
Yorlen_Zhang13 小时前
Python Tkinter Text 控件完全指南:从基础编辑器到富文本应用
开发语言·python·c#
不绝19113 小时前
C#进阶:预处理指令/反射,Gettype,Typeof/关键类
开发语言·c#
大鹏说大话14 小时前
告别 MSBuild 脚本混乱:用 C# 和 Nuke 构建清晰、可维护的现代化构建系统
开发语言·c#
风指引着方向14 小时前
归约操作优化:ops-math 的 Sum/Mean/Max 实现
人工智能·wpf
czhc114007566315 小时前
通信 28
c#
永远是我的最爱17 小时前
基于.NET的小小便利店前台收银系统
前端·sqlserver·.net·visual studio
听麟17 小时前
HarmonyOS 6.0+ 跨端智慧政务服务平台开发实战:多端协同办理与电子证照管理落地
笔记·华为·wpf·音视频·harmonyos·政务