为啥 Dapper 在有/没有迷你分析器连接的情况下为 Postgres 生成不同的 SQL

Posted

技术标签:

【中文标题】为啥 Dapper 在有/没有迷你分析器连接的情况下为 Postgres 生成不同的 SQL【英文标题】:Why does Dapper generate different SQL for Postgres with/without a mini-profiler connection为什么 Dapper 在有/没有迷你分析器连接的情况下为 Postgres 生成不同的 SQL 【发布时间】:2014-04-28 23:18:50 【问题描述】:

Dapper(1.13 Noobget 包)创建不同的 SQL 语句,具体取决于它是用于普通的 ADO.NET 数据库连接还是用于修饰的 mini-profiler 数据库连接。

示例代码(用 Postgresql 测试)

用途:

using System.Linq;
using Dapper;
using Npgsql;
using NUnit.Framework;
using StackExchange.Profiling;
using StackExchange.Profiling.Data;

Test1 使用普通的 ADO.NET 连接并且失败

[TestFixture]
public class DapperTests 
  private const string cnnstr = "HOST=...;DATABASE=...;USER ID=...;PASSWORD=...;";

  [Test]
  public void Test1() 
    using (var cnn = new NpgsqlConnection(cnnstr)) 
      cnn.Open();

      // The following line fails:
      cnn.Query<int>("SELECT 1 WHERE 42 IN @Items", new Items = new[] 41, 42, 43).Single();

      // Npgsql.NpgsqlException : ERROR: 42883: operator does not exist: integer = integer[]
    
  

Test2 使用围绕 ADO.NET 连接的小型分析器连接,并且成功

  [Test]
  public void Test2() 
    using (var cnn = new NpgsqlConnection(cnnstr))
    using (var profiled = new ProfiledDbConnection(cnn, MiniProfiler.Start())) 
      profiled.Open();

      int result = profiled.Query<int>("SELECT 1 WHERE 42 IN @Items", new Items = new[] 41, 42, 43).Single();

      Assert.AreEqual(1, result);
    
  

查看生成的 SQL 就很清楚为什么 Test1 失败了:

Test1 的 SQL:SELECT 1 WHERE 42 IN ((array[41,42,43])::int4[]) Test2 的 SQL:SELECT 1 WHERE 42 IN (((41)),((42)),((43)))

数组不支持 IN。

为什么 dapper 在使用/不使用配置文件连接时会生成不同的 SQL?

为什么它会生成一个带有普通连接的数组[...]?由于dapper's docs,它应该生成一个元组:

【问题讨论】:

在这两种情况下,您究竟是从哪里获取 sql 的?我想知道差异是否主要在于它的呈现方式。无论此处的提供者如何,Dapper 都会做同样的事情 这是第二次报告与 postgres 相关;我怀疑它实际上是 postgres 提供程序在这里弄得一团糟,因为那根本不是 dapper 发出的;我将不得不调查 我直接从服务器日志中获取 sql(在启用了 sql 日志记录的控制台中运行它)。导致问题的原因是 sql 根据是否使用/未使用配置文件的连接而有所不同.. PS:我注意到 mini-profiler 实际上包含一个 dapper 的副本(它在 StackExchange.Profiling.Helpers.Dapper 中是公开的)。无论如何,这可以安全地用于使用迷你分析器的项目中,还是有可能在以后变成内部的?我认为这个版本的 dapper 包含一些(有用的)更改/错误修复,这些更改/错误修复未包含在官方 dapper 中。 @MarcGravell 我调查并找到了问题的原因,但没有很好的解决方案。请在下面查看我的答案。 【参考方案1】:

Dapper 中有一个“FeatureSupport”类,其中包含对数组进行特殊处理的设置。 Postgresql 连接被标记为支持数组,而其他连接类型(包括 MiniProfiler ProfiledDbConnections)被标记为不支持数组。

如果连接不支持数组,Dapper 会手动为数组中的每个项目创建一个参数(如文档中所述) - 它成为 SQL 中的元组,如:SELECT 1 WHERE 42 IN (41, 42,43)

如果连接支持数组(如 Postgres 的 NpgsqlConnection),则数组参数会直接传递给连接,从而产生如下丑陋的结果:SELECT 1 WHERE 42 IN ('41,42,43':: int4[]) - 实际上失败了,因为 IN 不支持数组。

相关代码在SqlMapper.PackListParameters方法中。

因此在 ProfiledDbConnections 和 NpgsqlConnections 之间切换会导致问题,因为生成的 SQL 会不同。

为了摆脱 Postgres 连接中的数组语法,可以使用以下代码(尽管它只在全局级别上工作......):

using Dapper;
using Npgsql;

using (var cnn = new NpgsqlConnection())
  FeatureSupport.Get(cnn).Arrays = false;

似乎没有办法在每个查询或每个参数级别启用/禁用数组语法。

PS.:我在 https://code.google.com/p/dapper-dot-net/issues/detail?id=107&q=postgres 发现了这个问题的问题

【讨论】:

今天学到了;谢谢;哇,我需要清除拉取请求和问题! 谢谢!这解决了我的错误问题:运算符不存在:整数 = 整数 []。很棒的提示,不知道 FeatureSupport。 @MarcGravell 2019 打招呼...... :) 这似乎仍然是一个问题,并且在当前版本的 Dapper(2.0.30) 中,FeatureSupport 类被标记为内部 - 所以我可以不要再用这个了。还有其他方法吗? @Krummelz 现在;不;这属于我们希望在下一个专业中改进的提供者知识库(这将需要 API 大修),但是:它现在不存在

以上是关于为啥 Dapper 在有/没有迷你分析器连接的情况下为 Postgres 生成不同的 SQL的主要内容,如果未能解决你的问题,请参考以下文章

为啥profibus通信在有终端电阻情况下通讯异常

Dapper 如何在没有 setter 的情况下设置属性

为啥mysql主从复制,从刚开始能复制一会就不行了

为啥实体框架在直接选择语句中的执行速度比 Dapper 快 [关闭]

为啥我从 Dapper 返回的对象具有 null 和默认属性值?

Stack Overflow 的 MVC 迷你分析器(安装)