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

Rol*_*rgi 13 c# entity-framework contains enumerable

实体框架5+应该预先编译所有查询.但是,对于诸如此类的查询

List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();
Run Code Online (Sandbox Code Playgroud)

实体框架无法预编译查询,并且根据整体查询的复杂性,将表达式树解析为SQL可能会消耗几秒钟.有没有人找到一个解决方法来获得预编译的查询?我真的不明白为什么会这么难; 当然很难用参数,因为元素的数量可以不同,但​​是像SQL那样就足够了

SELECT a, b, c from MyEntities
WHERE c in __PLACEHOLDER__
Run Code Online (Sandbox Code Playgroud)

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

Aka*_*ava 7

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

SELECT A FOM B WHERE C IN @p 
Run Code Online (Sandbox Code Playgroud)

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

SELECT A FROM B WHERE C IN (@p1, @p2, @p3 ... etc) 
Run Code Online (Sandbox Code Playgroud)

此查询具有可变数量的参数,这就是原因,没有办法预编译此查询IEnumerable.Contains.

唯一的另一种选择(很长的路要走)是使用Xml或Json(在Sql 2016中推出).

将IEnumerable保存为xml.

[10,20,20,50] can be translated to
<data>
   <int value="10"/>
   <int value="20"/>
   <int value="20"/>
   <int value="50"/>
</data>
Run Code Online (Sandbox Code Playgroud)

然后,您可以将VIEW参数定义为

SELECT A FROM B WHERE C IN(从Xml中选择INT(@ P1))

并且您可以使用此视图,但是在EF中如何触发此查询存在更多挑战,但此查询可以预编译,因为它只有一个参数.

自定义SQL for Performance Hack

对于非常简单的查询,

List<Guid> ids;
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray();
Run Code Online (Sandbox Code Playgroud)

我可以简单地使用自定义SQL和fire,

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());
Run Code Online (Sandbox Code Playgroud)

临时表

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

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));
Run Code Online (Sandbox Code Playgroud)