问题描述


在SQL WHERE子句中使用Dapper参数时,该参数似乎区分大小写.然而,当我用字符串文字替换Dapper参数时,WHERE子句不再区分大小写.我创建了一个简单的ASP.NET核心recipe example web API来帮助说明这个问题.

在这个配方示例中,我使用了一个PostgreSQL数据库,并希望查询一个recipe table以按名称获取配方.我制作了citext类型的name列,这是一种不区分大小写的字符串类型.


数据库表


recipe表说明:

+-------------+--------+-----------+----------+--------------------+
|   Column    |  Type  | Collation | Nullable |      Default       |
+-------------+--------+-----------+----------+--------------------+
| recipe_id   | uuid   |           | not null | uuid_generate_v4() |
| name        | citext |           | not null |                    |
| description | text   |           |          |                    |
+-------------+--------+-----------+----------+--------------------+

recipe表的内容包括:

+--------------------------------------+--------------------+-----------------------------------------------------------+
|              recipe_id               |        name        |                        description                        |
+--------------------------------------+--------------------+-----------------------------------------------------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs          | balled up meat                                            |
| f44c696f-a94a-4f17-a387-dd4d42f60ef8 | red beans and rice | yummy new orleans original                                |
| 82c5911b-feec-4854-9073-6a85ea793dc0 | pasta cereal       | couscous and ground meat eaten with a spoon, like cereal! |
+--------------------------------------+--------------------+-----------------------------------------------------------+

查询方法


RecipeController有一个GetByName方法,该方法接受name参数作为URI路径的一部分.GetByName方法调用RecipeRepository类的GetByNameAsync方法,其中包含有问题的SQL语句:

public async Task<Recipe> GetByNameAsync(string name)
{
    string sql = $@"
                    SELECT  *
                    FROM    {nameof(Recipe)}
                    WHERE   {nameof(Recipe)}.{nameof(Recipe.name)} = @{nameof(name)}";
    
    using (IDbConnection connection = Open())
    {
        IEnumerable<Recipe> recipes = await connection.QueryAsync<Recipe>(sql, new {name});

        return recipes.DefaultIfEmpty(new Recipe()).First();
    }
}

查询响应


如果我想按名称查询meatballs食谱,并将name参数设置为"meatballs",我会得到以下响应:

{
  "recipe_id": "8f749e7a-e192-48df-91af-f319ab608212",
  "name": "meatballs",
  "description": "balled up meat"
}

name参数设置为"Meatballs",我得到以下响应:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  "title": "Not Found",
  "status": 404,
  "traceId": "00-5e4e35d5cfec644fc117eaa96e854854-c0490c8ef510f3b1-00"
}

最后,如果我用字符串文字"Meatballs"替换Dapper name参数:

public async Task<Recipe> GetByNameAsync(string name)
{
    string sql = $@"
                    SELECT  *
                    FROM    {nameof(Recipe)}
                    WHERE   {nameof(Recipe)}.{nameof(Recipe.name)} = 'Meatballs'";
    
    using (IDbConnection connection = Open())
    {
        IEnumerable<Recipe> recipes = await connection.QueryAsync<Recipe>(sql, new {name});

        return recipes.DefaultIfEmpty(new Recipe()).First();
    }
}

我得到以下回应:

{
  "recipe_id": "8f749e7a-e192-48df-91af-f319ab608212",
  "name": "meatballs",
  "description": "balled up meat"
}

Why is this Dapper parameter forcing case-sensitivity? And how can I get around this?

推荐答案


出身背景


正如Jeroen所指出的那样:

Presumably Dapper isn't doing any such thing and the same thing happens from any language where a parameter is passed as a regular string...

这确实不是Dapper的问题,而是SQL数据类型的问题.recipeExample数据库中的name列不知道传入的数据类型应该是citext类型.因此,有必要将传入参数强制转换为citext.

As Jeroen还指出:

From what I gather Postgres also supports collations, and using a case-insensitive collation on a regular string type is likely to work without conversion of any kind.

不知怎的,我错过了这个,但pgDocs甚至建议考虑nondeterministic collations而不是使用citext模块.在阅读了localizationcollationsthis YouTube video之后,我更新了recipe example web api,以使用citext模块与不确定排序进行比较.


数据库更新


首先,我添加了PostgreSQL文档中提供的case_insensitive排序规则:

CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false);

然后,我更新了recipe表,使其具有两个名称列:使用case_insensitive排序规则的text类型的name_1列和citext类型的name_2列:

+-------------+--------+------------------+----------+--------------------+
|   Column    |  Type  |    Collation     | Nullable |      Default       |
+-------------+--------+------------------+----------+--------------------+
| recipe_id   | uuid   |                  | not null | uuid_generate_v4() |
| name_1      | text   | case_insensitive | not null |                    |
| name_2      | citext |                  | not null |                    |
| description | text   |                  |          |                    |
+-------------+--------+------------------+----------+--------------------+
Indexes:
    "recipe_pkey" PRIMARY KEY, btree (recipe_id)
    "recipe_name_citext_key" UNIQUE CONSTRAINT, btree (name_2)
    "recipe_name_key" UNIQUE CONSTRAINT, btree (name_1)

接下来,我创建了三个Postgres函数来测试"Meatballs"查询:

  1. 第一个函数查询name_1列并接受text参数
  2. 第二个函数查询name_2列并接受text参数
  3. 第三个函数查询name_2列并接受citext参数

CREATE FUNCTION getrecipe_name1_text(text) RETURNS recipe as $$
    SELECT *
    FROM recipe
    WHERE recipe.name_1 = $1;
$$ LANGUAGE SQL;

CREATE FUNCTION getrecipe_name2_text(text) RETURNS recipe as $$
    SELECT *
    FROM recipe
    WHERE recipe.name_2 = $1;
$$ LANGUAGE SQL;

CREATE FUNCTION getrecipe_name2_citext(citext) RETURNS recipe as $$
    SELECT *
    FROM recipe
    WHERE recipe.name_2 = $1;
$$ LANGUAGE SQL;

查询测试


查询包含text个参数的name_1列:

recipeexample=# SELECT * FROM getrecipe_name1_text('Meatballs');

+--------------------------------------+-----------+-----------+----------------+
|              recipe_id               |  name_1   |  name_2   |  description   |
+--------------------------------------+-----------+-----------+----------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs | meatballs | balled up meat |
+--------------------------------------+-----------+-----------+----------------+
(1 row)

查询包含text个参数的name_2列:

recipeexample=# SELECT * FROM getrecipe_name2_text('Meatballs');

+-----------+--------+--------+-------------+
| recipe_id | name_1 | name_2 | description |
+-----------+--------+--------+-------------+
|           |        |        |             |
+-----------+--------+--------+-------------+
(1 row)

查询包含citext个参数的name_2列:

recipeexample=# SELECT * FROM getrecipe_name2_citext('Meatballs');

+--------------------------------------+-----------+-----------+----------------+
|              recipe_id               |  name_1   |  name_2   |  description   |
+--------------------------------------+-----------+-----------+----------------+
| 8f749e7a-e192-48df-91af-f319ab608212 | meatballs | meatballs | balled up meat |
+--------------------------------------+-----------+-----------+----------------+
(1 row)

结论


  1. 如果使用citext模块,查询时参数必须转换为citext
  2. 如果使用case_insensitive排序规则,将有性能损失,并且不可能进行模式匹配操作

Csharp相关问答推荐

如何使用PDFSharp将文本添加到现有PDF

C#类主构造函数中的调试参数

有没有一种方法可以在包含混合文本的标签中嵌入超链接?

try 还原包时出错:Dapper已经为System.Data.SQLClient定义了依赖项''''

如何修改中间件或其注册以正确使用作用域服务?

实体框架核心上是否支持使用NPGSQL的字符串聚合?

当通过Google的Gmail Api发送邮件时,签名会产生dkim = neutral(正文散列未验证)'

为什么将鼠标悬停在DateTimeOffset上只显示Hour值?

具有单一导航属性的EF核心一对多关系

从Blob存储中提取tar.gz文件并将提取结果上载到另一个Blob存储

C#EF Core 8.0表现与预期不符

HttpConext.Request.Path和HttpConext.GetEndpoint()之间的差异

在EF Core中,有没有什么方法可以防止在查询中写入相同的条件,而代之以表达式?

单元测试:模拟返回空

在C#中反序列化/序列化具有混合元素顺序的XML时出现问题

在.NET 7.0 API控制器项目中通过继承和显式调用基类使用依赖项注入

如何比较C#中的L和ł(波兰字符)返回TRUE

具有以接口为其类型的属性的接口;类指定接口的实现,但无效

如何将%{v_扩展}转换为%{v_扩展}>>

在C#中通过Matheval使用自定义公式