Oly*_*Oly 7 c# sql sql-server entity-framework parameterized-query
我希望能够使用使用表值参数的实体框架构建参数化的临时 SQL 查询。
注意:引起我兴趣的用例是在给定 ID 列表的情况下查询多个实体。我希望查询计划程序能够在可能的情况下缓存计划,但我不一定要创建存储过程。
假设我有一些 id:
IEnumerable<int> ids = new [] {0, 42, -1};
Run Code Online (Sandbox Code Playgroud)
如果我编写一个 EF 查询,例如
context.MyEntities
.Where(e => ids.Contains(e.Id))
Run Code Online (Sandbox Code Playgroud)
生成的 sql未参数化,如下所示:
SELECT
[Extent1].[Name] AS [Name]
FROM [MyEntities] AS [Extent1]
WHERE [Extent1].[Id] IN (0, 42, -1)
Run Code Online (Sandbox Code Playgroud)
我想要得到的是类似的东西
SELECT
[Extent1].[Name] AS [Name]
FROM [MyEntities] AS [Extent1]
WHERE EXISTS (SELECT
1
FROM @ids AS [Extent2]
WHERE [Extent2].[Id] = [Extent1].[Id]
)
Run Code Online (Sandbox Code Playgroud)
这是完全参数化的。
这可以在 EF 即席查询中完成吗?
我知道可以使用EF 传递表值参数来直接查询(例如存储过程),使用 withSqlParameter和SqlDbType.StructuredaDataTable作为其值(请参阅/sf/answers/728679731/) 。当我尝试使用相同的技巧来创建IQueryablemy 的版本时ids,我惊讶地发现生成的 SQL 实际上枚举了值,因此它看起来像我给出的第一个(不需要的)SQL 示例!The SqlParameter is already contained by another SqlParameterCollection当我尝试执行查询时它也会抱怨。
一种可行的方法是将IEnumerableID 转换为IQueryable以下方式:
joinedMyStringSplit)public class IntId { public int Id { get; set; } }((IObjectContextAdapter)context).ObjectContext.CreateQuery<IntId>("MyStringSplit(@joined)", new ObjectParameter("joined", joined))创建IQueryable我的 ID。这会产生类似的东西
SELECT
[Extent1].[Name] AS [Name]
FROM [MyEntities] AS [Extent1]
WHERE [Extent1].[Id] IN (SELECT
1
FROM [MyStringSplit](@joined) AS [Extent2]
WHERE [Extent2].[Id] = [Extent1].[Id]
)
Run Code Online (Sandbox Code Playgroud)
这与我所追求的很接近,但是很混乱,并且肯定不能提供实际表值参数的性能优势。
编辑:为了澄清,我想到的是某种很好的 c# 端抽象,我可以用它来将我的IEnumerable集合“转换”为IQueryable表示形式(对于特定上下文),当 EF 使用时,这些表示形式会被解释为表值参数。我们可以假设必要的表类型已经在 SQL 端定义(例如整数 ID 的表类型、字符串 ID 的表类型...)
我们遇到了类似的问题,查询存储被这些类型的动态查询(基于变量“IN”子句)填满。因此,我们将其从 EF 查询更改为内联 SQL,使其成为参数化查询,从而使用单个查询计划。
IEnumerable<int> ids = new [] {0, 42, -1};
var strIds = string.Join(",", ids);
var names = context.Database.SqlQuery<string>(@"SELECT [NAME]
FROM [MyEntities] as e
join STRING_SPLIT(@ids, ',') as i on e.Id = o.value",
new SqlParameter("@ids", strIds));
Run Code Online (Sandbox Code Playgroud)
注意:STRING_SPLIT 可在兼容性级别 130 或更高版本上使用,参见https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-版本15