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

相关推荐
zhanshuo37 分钟前
5分钟手把手实战:用HTML5基础结构打造你的个人简介页面
.net
界面开发小八哥40 分钟前
界面开发框架DevExpress XAF实践:集成.NET Aspire后如何实现数据库依赖?
ui·.net·界面控件·devexpress·ui开发·xaf
zhanshuo41 分钟前
5分钟搞定!ASP.NET正则表达式验证控件实战:轻松拦截99%的无效邮箱!
.net
阿翰2 小时前
自动 GitHub Readme 20 种语言翻译平台 - OpenAiTx 开源免费
c#·.net
枫叶kx6 小时前
1Panel运行的.net程序无法读取系统字体(因为使用了docker)
c#
军训猫猫头11 小时前
96.如何使用C#实现串口发送? C#例子
开发语言·c#
不爱写代码的玉子13 小时前
HALCON透视矩阵
人工智能·深度学习·线性代数·算法·计算机视觉·矩阵·c#
明耀15 小时前
WPF DataGrid 默认显示行号
wpf
开开心心就好15 小时前
高效Excel合并拆分软件
开发语言·javascript·c#·ocr·排序算法·excel·最小二乘法
一名用户17 小时前
unity实现自定义粒子系统
c#·unity3d·游戏开发