【问题标题】:How does LINQ expression syntax work with Include() for eager loadingLINQ 表达式语法如何与 Include() 一起使用以进行预加载
【发布时间】:2011-07-20 11:23:23
【问题描述】:

我在下面有一个查询,但我想执行 Include() 来急切加载属性。 Actions有一个导航属性User(Action.User)

1) 我的基本查询:

from a in Actions
join u in Users on a.UserId equals u.UserId
select a

2) 第一次尝试:

from a in Actions.Include("User")
join u in Users on a.UserId equals u.UserId
select a

但 Action.User 没有填充。

3) 尝试在查询之外将“用户”加载到导航属性中:

(from a in Actions
join u in Users on a.UserId equals u.UserId    
select a).Include("User")

在 LINQPad 中尝试 Include 时出现错误:

“System.Linq.IQueryable”不包含“Include”的定义,并且找不到接受“System.Linq.IQueryable”类型的第一个参数的扩展方法“Include”(按 F4 添加 using 指令或程序集参考)

我认为这是因为 LINQ 不支持 Include()。

所以我在VS中尝试了;查询 2 运行,但返回未填充的用户属性。 查询 3 的扩展方法似乎不存在,尽管它在没有查询的情况下存在于 Action 本身上。

【问题讨论】:

  • 动作模型有一个名为“用户”的属性?你有 DbContext 吗?

标签: c# linq entity-framework linqpad


【解决方案1】:

我想通了,还是谢谢你的建议。 解决方案是这样做(我的问题中的第二次尝试):

var qry = (from a in Actions
join u in Users on a.UserId equals u.UserId    
select a).Include("User")

智能感知在查询后没有显示包含的原因是因为我需要以下使用:

using System.Data.Entity;

这样做一切正常。

【讨论】:

  • 假设您的实体在上下文中正确映射,连接将自动发生。您可以通过更简单的查询来实现相同的目的:var qry = from a in Actions.Include("User") select a 如果您要加入未明确映射为导航属性的实体(即:操作没有用户属性,或者由于某种原因未映射为导航属性) ,那么您将需要加入,删除包含,然后您可以使用select new { Action = a, User = u } 让 EF 返回加入的数据。 (后者一般称为Projection)
【解决方案2】:

更好的、重构友好的代码 (EF6)

using System.Data.Entity;
[...]
var x = (from cart in context.ShoppingCarts
         where table.id == 123
         select cart).Include(t => t.CartItems);

var x = from cart in context.ShoppingCarts.Include(nameof(ShoppingCart.CartItems))
        where table.id == 123
        select cart;

2017 年 3 月 31 日更新

您也可以在 lambda 语法中对任一方法使用 include:

var x = from cart in context.ShoppingCarts.Include(p => p.ShoppingCart.CartItems))
        where table.id == 123
        select cart;

【讨论】:

  • 感谢nameof()。我不断看到硬编码的表名字符串。
  • 在他们在 C#6 中添加的所有功能中,nameof 是我使用最多的一个!感谢您的反馈!
【解决方案3】:

如果您想要的是一个查询,它将返回所有 Action 实体,其关联的 User 实体实际上通过 Action.UserId foreign key property 存在,这将做到:

var results = context.Actions
    .Include("User")
    .Where(action =>
        context.Users.Any(user =>
            user.UserId == action.UserId));

但是您不必使用外键属性来进行过滤,因为您也有navigation properties。因此,您可以通过过滤 Action.User 导航属性来简化您的查询,如下例所示:

var results = context.Actions
    .Include("User")
    .Where(action => action.User != null);

如果您的模型声明Action.User 属性永远不能为空(即Action.UserId 外键在数据库中不可为空)并且您想要的实际上是所有Action 实体及其关联的Users,那么查询就变得更简单了

var results = context.Actions.Include("User");

【讨论】:

  • 您好,您的示例都显示了链式方法,我的查询是上面显示的表达式语法(使用 from/select)。你能告诉我怎么做吗?
【解决方案4】:

执行您发布的问题中提到的基本查询,除非您返回匿名类型,否则您将无法看到 User 属性,如下所示:

from a in Actions
join u in Users on a.UserId equals u.UserId
select new
{
   actionUserId = a.UserId
   .
   .
   .
   userProperty1 = u.UserId
};

但是,如果要在 ObjectContext 上使用 Include 方法,您可以使用以下方法:

使用以下行确保您已关闭 LazyLoading:

entities.ContextOptions.LazyLoadingEnabled = false;

然后继续

var bar = entities.Actions.Include("User");
var foo = (from a in bar
           select a);

【讨论】:

  • 据我了解,Include("User") 是为了触发急切加载,所以用LazyLoadingEnabled = false 关闭延迟加载充其量是多余的,但最终会误导。我认为,如果您要交换“foo”和“bar”,那么 Include 将在 EF 查询上工作并急切加载附加表。
【解决方案5】:

我为此使用了 LoadWith 选项

var dataOptions = new System.Data.Linq.DataLoadOptions();
dataOptions.LoadWith<Action>(ac => as.User);
ctx.LoadOptions = dataOptions;

就是这样。 ctx 是您的数据上下文。这对我有用:-)

【讨论】:

  • 这个例子使用 LINQ to SQL,而问题是关于实体框架的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-05
相关资源
最近更新 更多