是否可以将DataTable传递给Entity Framework中的ad-hoc sql查询?

时间:2017-10-26 15:55:41

标签: c# sql sql-server entity-framework parameterized-query

我希望能够使用实体框架构建参数化的临时SQL查询,该实体框架使用表值参数。

注意:引起我兴趣的用例是在给定ID列表的情况下查询多个实体。我希望查询规划器能够在可能的情况下缓存计划,但我不一定要创建存储过程。

假设我有一些ID:

context.MyEntities
    .Where(e => ids.Contains(e.Id))

如果我写一个像

这样的EF查询
SELECT
    [Extent1].[Name] AS [Name]
    FROM [MyEntities] AS [Extent1]
    WHERE [Extent1].[Id] IN (0, 42, -1)

生成的sql 参数化,如下所示:

SELECT
    [Extent1].[Name] AS [Name]
    FROM [MyEntities] AS [Extent1]
    WHERE EXISTS (SELECT
        1
        FROM @ids AS [Extent2]
        WHERE [Extent2].[Id] = [Extent1].[Id]
    )

我想要的是

SqlParameter

是完全参数化的。

可以在EF即席查询中完成吗?

我知道 可以将表值参数传递给使用EF的直接查询(例如,存储过程),使用SqlDbType.StructuredDataTable并且IQueryable作为其值(请参阅https://stackoverflow.com/a/10409710/5181199)。当我尝试使用相同的技巧来创建ids The SqlParameter is already contained by another SqlParameterCollection版本时,我惊讶地发现生成的SQL实际上枚举了值,因此它看起来像第一个(不需要的)SQL示例我给!当我尝试执行查询时,它也会抱怨IEnumerable

一种简单易用的方法是按以下方式将IQueryable ID转换为joined

  • 将值加入单个分隔字符串MyStringSplit
  • 在数据库端使用表值输出进行某种字符串分割和解析功能(public class IntId { public int Id { get; set; } }
  • 创建EF&#39;复杂类型&#39;对于上述函数的输出结构,如((IObjectContextAdapter)context).ObjectContext.CreateQuery<IntId>("MyStringSplit(@joined)", new ObjectParameter("joined", joined))
  • 使用IQueryable创建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] ) 我的ID。

这会产生类似

的东西
IEnumerable

接近我之后的情况,但是很麻烦,肯定不能提供实际表值参数的性能优势。

编辑:为了澄清,我想到的是一些很好的c#-side抽象,我可以使用它来转换&#39;我的IQueryable集合到{{1}}表示(对于特定上下文),当被EF使用时,它们被解释为表值参数。我们可以假设已经在SQL端定义了必要的表类型(例如,整数ID的表类型,字符串ID的表类型......)

1 个答案:

答案 0 :(得分:0)

我们有一个类似的问题,查询存储中已经充满了这类动态查询(基于变量“ 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));

注意:根据https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql?view=sql-server-ver15,STRING_SPLIT的兼容级别为130或更高