mat*_*ati 4 c# linq postgresql entity-framework-core
我正在尝试使用 .NET 3.1 上的 EF Core。我有以下代码摘录:
public static async Task<Task> getWithName(HttpContext c) {
var name = c.Request.RouteValues["name"].ToString();
// with API - WORKS!
var authors = await DB.Authors.Where(a => a.first_name.Contains(name)).ToListAsync();
// with raw SQL interpolated - BROKEN
var authors2 = await DB.Authors.FromSqlInterpolated($"SELECT * FROM author WHERE first_name like '%{name}%'").ToListAsync();
// with raw SQL parametrized - BROKEN
var authors3 = await DB.Authors.FromSqlRaw("SELECT * FROM author WHERE first_name like '%{0}%'", name).ToListAsync();
// with LINQ expressions - WORKS!
var authors4 = await (from a in DB.Authors where a.first_name.Contains(name) select a).ToListAsync();
c.Response.ContentType = "application/json";
return c.Response.WriteAsync(Serialize(authors));
}
Run Code Online (Sandbox Code Playgroud)
除了该方法缺少上下文之外,让我感到困惑的是,Fluent API 版本和 LINQ 查询表达式版本都按预期工作,返回数据库中的 12 项。相反,插值 SQL 和原始 SQL 都无法返回 0 项。
请注意:这并不是我遇到异常或任何错误。它们只是返回 0 个结果,就好像查询是错误的一样。
为了进行原始测试,我只是放置了一个断点,复制名称值并直接在 pgAdmin 中执行查询。该查询按预期工作,返回相同的 12 个项目。
这是您在代码中看到的 4 个查询的(相当复杂的)调试输出:
#this is the DB init debug output
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.4 initialized 'AuthorContext' using provider
'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None
#this is the fluent API query, which works
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (14ms) [Parameters=[@__name_0='?'], CommandType='Text', CommandTimeout='30']
SELECT a.id, a.first_name, a.last_name, a.nationality
FROM public.author AS a
WHERE (@__name_0 = '') OR (STRPOS(a.first_name, @__name_0) > 0)
#this is the raw SQL, which fails
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (7ms) [Parameters=[p0='?'], CommandType='Text', CommandTimeout='30']
SELECT * FROM author WHERE first_name like '%@p0%'
#this is the interpolated SQL, which fails too
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[p0='?'], CommandType='Text', CommandTimeout='30']
SELECT * FROM author WHERE first_name like '%@p0%'
#this is the LINQ expression query, which works
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@__name_0='?'], CommandType='Text', CommandTimeout='30']
SELECT a.id, a.first_name, a.last_name, a.nationality
FROM public.author AS a
WHERE (@__name_0 = '') OR (STRPOS(a.first_name, @__name_0) > 0)
Run Code Online (Sandbox Code Playgroud)
我看到 LINQ/Fluent 版本以一种我无法理解的相当奇怪的 SQL 进行转换,但不幸的是,我也不明白为什么代码没有在原始/插值 SQL 中正确扩展我的参数。
谢谢您的任何提示!
好的,
我懂了!与此同时,ErikEJ 在评论中做出了回应。
TL; DR:基本上您需要将通配符包含到原始 C# 变量中
在我原来的代码中存在很多问题:
SELECT * FROM author WHERE first_name like '%matteo%'
Run Code Online (Sandbox Code Playgroud)
不管怎样,任何数据库连接层都可能面向准备好的语句而不是原始查询,因此我在 SQL 中尝试了这个新代码:
DEALLOCATE foo;
PREPARE foo (text) AS
SELECT * FROM author WHERE first_name like '%$1%';
execute foo('matteo');
Run Code Online (Sandbox Code Playgroud)
这在 SQL 中也失败了!
DEALLOCATE foo;
PREPARE foo (text) AS
SELECT * FROM author WHERE first_name like $1;
execute foo('%matteo%');
Run Code Online (Sandbox Code Playgroud)
所以我尝试过这段代码:
public static async Task<Task> getWithName(HttpContext c) {
var name = c.Request.RouteValues["name"].ToString();
var name2 = "'%"+name+"%'"; //<- please notice the single quote for SQL strings!
// with API - WORKS!
var authors = await DB.Authors.Where(a => a.first_name.Contains(name)).ToListAsync();
// with raw SQL interpolated - BROKEN
var authors2 = await DB.Authors.FromSqlInterpolated($"SELECT * FROM author WHERE first_name like {name2}").ToListAsync();
// with raw SQL parametrized - BROKEN
var authors3 = await DB.Authors.FromSqlRaw("SELECT * FROM author WHERE first_name like {0}", name2).ToListAsync();
// with LINQ expressions - WORKS!
var authors4 = await (from a in DB.Authors where a.first_name.Contains(name) select a).ToListAsync();
c.Response.ContentType = "application/json";
return c.Response.WriteAsync(Serialize(authors));
}
Run Code Online (Sandbox Code Playgroud)
不幸的是这又失败了。
DEALLOCATE foo;
PREPARE foo (text) AS
SELECT * FROM author WHERE first_name like $1;
execute foo(''%matteo%''); //<- double single quote caused by the EF Core automatic cast.
Run Code Online (Sandbox Code Playgroud)
所以问题的解决方案是:
public static async Task<Task> getWithName(HttpContext c) {
var name = c.Request.RouteValues["name"].ToString();
var name2 = "%"+name+"%"; //<- please notice: NO MORE single quote!
// with API - WORKS!
var authors = await DB.Authors.Where(a => a.first_name.Contains(name)).ToListAsync();
// with raw SQL interpolated - BROKEN
var authors2 = await DB.Authors.FromSqlInterpolated($"SELECT * FROM author WHERE first_name like {name2}").ToListAsync();
// with raw SQL parametrized - BROKEN
var authors3 = await DB.Authors.FromSqlRaw("SELECT * FROM author WHERE first_name like {0}", name2).ToListAsync();
// with LINQ expressions - WORKS!
var authors4 = await (from a in DB.Authors where a.first_name.Contains(name) select a).ToListAsync();
c.Response.ContentType = "application/json";
return c.Response.WriteAsync(Serialize(authors));
}
Run Code Online (Sandbox Code Playgroud)
基本上您需要将通配符包含到原始 C# 变量中
新问题:除了这个丑陋的通配符包含之外,还有其他解决方案吗?!
| 归档时间: |
|
| 查看次数: |
2331 次 |
| 最近记录: |