【问题标题】:Entity Framework: When will a variable be evaluated实体框架:何时评估变量
【发布时间】:2016-01-25 20:44:53
【问题描述】:

我遇到了 EF 和 IQueryable 的奇怪行为。

public IQueryable<foo> getFoo()  
{ 
  IQueryable<foo> query;
  string someVar = functies.getSomeInt().ToString();
  try
  {       
      query = from sTable in db.someTable
              from oTable in db.otherTable
              where sTable.Id == oTable.Id
              && sTable.Var == someVar
             select sTable;
   } catch {}
   return query;
}

public Test()
{
   var queryFoo =  getFoo();
   foreach(var foo in queryFoo)
   {
      //This works fine
   }
}

上面的例子运行得很好,但是当你将 someVar 的声明放在 try 块中时,EF 似乎无法将 sTable.Var 与 someVar 进行比较。

public IQueryable<foo> getFoo()  
{ 
  IQueryable<foo> query;
  try
  {       
      string someVar = functies.getSomeInt().ToString();
      query = from sTable in db.someTable
              from oTable in db.otherTable
              where sTable.Id == oTable.Id
              && sTable.Var == someVar
             select sTable;
   } catch {}
   return query;
}

public Test()
{
   var queryFoo =  getFoo();
   foreach(var foo in queryFoo)
   {
      //This doesn't work
      //unable to create constant value of type 'foo'
   }
}

这会导致“无法创建类型为 'foo' 的常量值。在此上下文中仅支持原始类型或枚举类型。”

对于 EF 为何如此行事,是否有任何合乎逻辑的解释? 我真的不明白其中的区别,它可能与变量的范围有关。但这似乎仍然很奇怪。

到目前为止,我做了哪些研究(因此,如果我按照以下示例进行操作,则 someVar 在嵌套更深时不是本地的???):

局部变量,比如下面的orderID变量 例如,在客户端上进行评估。

int orderID = 51987;

IQueryable<SalesOrderHeader> salesInfo =
from s in context.SalesOrderHeaders
where s.SalesOrderID == orderID
select s;

还有

方法参数也在客户端进行评估。传入 MethodParameterExample 方法的 orderID 参数。

public static void MethodParameterExample(int orderID)
{
 using (AdventureWorksEntities context = new AdventureWorksEntities())
 {

    IQueryable<SalesOrderHeader> salesInfo =
        from s in context.SalesOrderHeaders
        where s.SalesOrderID == orderID
        select s;                

    foreach (SalesOrderHeader sale in salesInfo)
    {
        Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
    }
 }
}

Literals and Parametesr on the Client

未更改:

public IQueryable<klassen> getKlassenPerGebruiker(Db.Models.gebruiker gbr)
{
 // Connectie met de database maken.
 var db = new Datastore.Db.Models.dataContext(); //of type DbContext
 IQueryable<klassen> query;

 try
 {
    string jaar = functies.getHuidigStartJaar().ToString();
    query = from klas in db.klassens
            from kpg in db.klassenpergebruikers
            from sj in db.schooljaars
            where kpg.Klassen_id == klas.Id
               && kpg.Gebruikers_id == gbr.Id
               && sj.Id == klas.Schooljaar_id
               && sj.Start == jaar
               && kpg.School_id == gbr.School_id
               orderby klas.Naam
            select klas;
 }
 catch
 {
   query = null;
 }
}

调用函数;

 IQueryable qklassen = gs.getKlassenPerGebruiker((user) Session["user"]);

            if (qklassen != null)
            {
                foreach (klassen klas in qklassen)
                {
                }
            }

【问题讨论】:

  • 请学会像程序员一样思考。不要说“EF 似乎无法解决 someVar”。说出发生了什么(行为和/或错误消息)。
  • 你现在所拥有的不可能运行良好。如果您的查询构造引发异常,query 将保持未初始化状态,C# 编译器将检测到这种可能性并拒绝您的 return query; 语句。显示一些实际的工作代码以及非工作代码,其中 only 与工作代码之间的变化就是您要询问的内容。
  • @JonathanWood 更新了帖子。我对在 stackoverflow 上发布问题很陌生。
  • 我强烈的感觉是这些示例并不能完全代表您的真实代码。这不应该有任何区别。在现实中,您可能有一些方法返回“Foo”,该方法在IQueryable 执行之前或期间进行评估。
  • 嘿@GertArnold,这个例子不能更接近我实际所做的。涉及更多的表(加入 4 获胜)。我想指出的是,我没有返回“Foo”对象,而是在 getFoo() 方法之外评估的 IQueryable。我可以创建一个 testproject 并共享它来调用此行为。

标签: c# entity-framework iqueryable


【解决方案1】:

当你评估它时,它就会被评估。当您具体化查询(ToArray、FirstOrDefault 等)时会发生这种情况。您返回查询 - 这就是问题所在。

【讨论】:

  • 要扩展 TomTom 的出色答案,@yowlsmie 您应该对“可变范围”进行一些研究。
  • 嘿 TomTom,这是否意味着当我返回可查询对象时,查询是针对数据库执行的?为了扩大我的困惑,当 someVar 也已经被销毁时,我可以遍历返回的 Queryable。
  • @SamAxe 据我所知,如果我将 IQueryable 返回给另一个函数, someVar 已经被破坏了。但是EF对此没有任何问题。我错过了什么吗?
  • @yowslymie 是的,您错过了这样一个事实,即当您关闭 lambda 中的一个变量时,该变量不能被销毁,直到不再有对它的任何引用。只要有可以运行的代码可以访问变量,变量就必须始终存在,并且由于您的程序仍然有可以运行的代码可以在返回后访问它,因此当方法返回时无法清理该变量。跨度>
  • @Servy 好的,我明白了,但在这种情况下,我的两个示例应该具有相同的结果。因为两个 lambda 都引用了 someVar。唯一改变的是范围。对不起,如果我是一个完全的新手,但我仍然没有完全理解这两个例子之间的区别。
猜你喜欢
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
相关资源
最近更新 更多