为啥 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的主要内容,如果未能解决你的问题,请参考以下文章
为啥实体框架在直接选择语句中的执行速度比 Dapper 快 [关闭]