【问题标题】:Using Include() with inherited entities对继承的实体使用 Include()
【发布时间】:2010-02-23 11:03:58
【问题描述】:

在 EF 急切加载相关实体是easy

但在使用 table-per-type 模型加载数据时,我遇到了包括继承实体的困难。

这是我的模型:

实体

  • ArticleBase(基础文章实体)
    • ArticleSpecial(继承自ArticleBase
  • UserBase(基本用户实体)
    • UserSpecial(继承自UserBase
  • Image

关系如图所示(省略许多列):

实际上,我的用户总是UserSpecial 类型,因为UserBase 在另一个应用程序中使用,因此我们可以共享凭据。这是我有两个单独的表的唯一原因。 UserBase table 不能以任何方式改变形状或形式,因为其他应用程序会崩溃。

问题

我应该如何加载 ArticleSpecial 并设置 CreatedByEditedBy,以便两者都是 UserSpecial 类型(定义 Image 关系)?


我已经尝试过(虽然没有成功)这些选项:

1。 使用 lambda 表达式:

context.ArticleBases
    .OfType<ArticleSpecial>()
    .Include("UserCreated.Image")
    .Include("UserEdited.Image");

在这种情况下,问题在于CreatedByEditedBy 都与UserBase 相关,这并没有定义Image 导航。所以我应该以某种方式将这两个转换为UserSpecial 类型,例如:

context.ArticleBases
    .OfType<ArticleSpecial>()
    .Include("UserCreated<UserSpecial>.Image")
    .Include("UserEdited<UserSpecial>.Image");

当然,在Include("UserCreated&lt;UserSpecial&gt;.Image") 中使用泛型是行不通的。

2。 我尝试过使用 LINQ 查询

var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
                  join created in ctx.UserBase.OfType<UserSpecial>().Include("Image")
                  on articleSpecial.UserCreated.Id equals created.Id
                  join edited in ctx.UserBase.OfType<UserSpecial>().Include("Image")
                  on articleSpecial.UserEdited.Id equals edited.Id   
              select articleSpecial;

在这种情况下,我只得到ArticleSpecial 对象实例,而没有设置相关属性。我知道我应该以某种方式选择那些,但我不知道如何?

我的 LINQ 中的选择部分可以更改为类似

select new { articleSpecial, articleSpecial.UserCreated, articleSpecial.UserEdited };

但图像仍未加载到我的上下文中。在这种情况下,我的联接几乎不会用于过滤 articleSpecial 结果,但它们不会将实体加载到上下文中(我想)。

【问题讨论】:

  • 我的回答对您的问题有帮助吗?

标签: entity-framework inheritance join eager-loading


【解决方案1】:

这似乎是当前版本的 Entity Framework (1.0) 中的一个限制。看看this related SO question

在您的情况下,在投影中包含相关的 UserCreatedUserEdited 属性是正确的解决方案。但是,如果您还想在 UserSpecial 对象上填充 Image 属性,则必须确保也包含该属性:

var results = from articleSpecial in ctx.ArticleBase.OfType<ArticleSpecial>()
              select new
              {
                  articleSpecial,
                  articleSpecial.UserCreated,
                  ((UserSpecial)articleSpecial.UserCreated).Image,
                  articleSpecial.UserEdited,
                  ((UserSpecial)articleSpecial.UserEdited).Image
              };

当然,此查询建立在所有 ArticleSpecial 实体始终引用 UserSpecial 实体的假设之上,否则转换将失败。
如果这个假设并不总是正确,您可以使用 LINQ 扩展方法和多行 lambda 函数来表达相同的查询来执行安全转换:

var results = ctx.ArticleBase
                 .OfType<ArticleSpecial>()
                 .AsEnumerable()
                 .Select(a =>
                  {
                      var userCreated = a.UserCreated as UserSpecial;

                      if (userCreated != null)
                      {
                          var image = userCreated.Image;
                      }

                      var userEdited = a.UserEdited as UserSpecial;

                      if (userEdited != null)
                      {
                          var image = userEdited.Image;
                      }

                      return a;
                  });

在后一个示例中,您也不需要在结果中包含 UserSpecialImage 实体。相反,您只需要在投影阶段访问 ArticleSpecial 实体上的导航属性,以强制 Entity Framework 急切加载相关对象。

【讨论】:

  • 我正在尝试做同样的事情,但我很困惑,因为问题中的模型中没有“UserCreated”。这是从哪里来的?
  • 另外,带有多行 lambda 的第二个选项给了我一个错误,表明它不能转换为表达式树。见:weblogs.asp.net/zeeshanhirani/archive/2008/07/10/…
  • @AaronLS 我相信“UserCreated”和“UserEdited”实际上是模型中描绘的导航属性“CreatedBy”和“EditedBy”。它们可能已重命名:-)
  • @AaronLS 你是对的。该表达式无法转换为 SQL 语句。在这种情况下,我们必须通过查询 IEnumerable 而不是 IQueryable 来使用 LINQ to Objects。我更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
相关资源
最近更新 更多