实体框架:Enumerable.Contains的预编译查询

本文关键字:编译 查询 Contains 框架 Enumerable 实体 | 更新日期: 2023-09-27 18:17:50

实体框架5+应该预编译所有查询。但是,对于像

这样的查询,
List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();

Entity Framework不能预编译查询,并且根据整个查询的复杂程度,将表达式树解析为SQL可能需要几秒钟的时间。有没有人找到一个变通方法来获得预编译查询?我真的不明白为什么会这么难;当然,使用参数很难做到这一点,因为元素的数量可以不同,但是使用像

这样的SQL就足够了。
SELECT a, b, c from MyEntities
WHERE c in __PLACEHOLDER__

,然后用实际的列表元素替换占位符。当然,它不像传递参数那么好,但它比等待几秒钟反复解析整个表达式树要好得多。

实体框架:Enumerable.Contains的预编译查询

您必须首先了解"IN"操作符在参数化SQL查询中的工作原理。

SELECT A FOM B WHERE C IN @p 

不起作用,SQL命令参数不接受ARRAY作为参数值,而是将查询转换为

SELECT A FROM B WHERE C IN (@p1, @p2, @p3 ... etc) 

这个查询有可变数量的参数,这就是为什么没有办法用IEnumerable.Contains预编译这个查询。

唯一的其他选择(很长很长的路)是使用Xml或Json(将在Sql 2016中出现)。

Save your IEnumerable as xml.

[10,20,20,50] can be translated to
<data>
   <int value="10"/>
   <int value="20"/>
   <int value="20"/>
   <int value="50"/>
</data>

然后你可以定义一个参数为

VIEW

SELECT INT FROM Xml(@P1)

你可以使用这个视图,但是在EF中有更多的挑战,如何触发这个查询,但是这个查询可以预编译,因为它只有一个参数。

自定义SQL for Performance Hack

对于非常简单的查询,如

List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();

我可以简单地使用自定义SQL语句,然后输入

var parameterList = ids.Select( 
   (x,i)=> new SqlCommandParameter(
      "@p"+i, x));
var pnames = String.Join(",", parameterList.Select(x=> x.ParameterName));
var entities = 
    context.SqlQuery<MyEntity>(
       "SELECT * FROM TABLE WHERE Id in (" + pnames + ")",
        parameterList.ToArray());
临时表

也可以使用临时表,但这会增加数据库中活动事务的数量。

Guid sid = Guid.NewGuid();
foreach(var p in ids){
    db.TempIDs.Add(new TempID{ SID = sid, Value = p });
}
db.SaveChanges();
var qIDs = db.TempIDs.Where( x=> x.SID == sid );
var myEntities db.MyEntities.Where( x => qIDs.Any( q.Value == x.Id) );
// delete all TempIDs...
db.SqlQuery("DELETE FROM TempIDs WHERE SID=@sid,
     new SqlCommandParameter("@sid", sid));