【问题标题】:Entity Framework 4 Abstract Model - How to Programatically Eager-Load Navigational Properties?实体框架 4 抽象模型 - 如何以编程方式急切加载导航属性?
【发布时间】:2010-10-25 00:39:12
【问题描述】:

我有一个用抽象实体/类构建的 EF4 模型:

注意 State 实体如何具有名为 Country 的导航属性。

注意:我禁用了延迟加载,所以我必须按需加载。

现在,如果我有以下方法:

public Location FindSingle(int id)
{
   return _repository.Find().WithId(id).SingleOrDefault();
}

默认情况下,这不会返回任何关联。但是当我明确想要时,我如何动态地预先加载关联?

我不能这样做:

return _repository.Find().WithId(id).Include("Country").SingleOrDefault();

当我使用一个名为 Location 的抽象类时,它没有名为“Country”的导航属性。在我使用.SingleOrDefault 实际执行查询之前,我知道派生类型是什么。

所以,这就是我必须做的:

public Location FindSingle(int id, bool includeAssociations = false)
{
    var location = _repository.Find().WithId(id).SingleOrDefault();
    return includeAssociations
                       ? LoadAssociation(location)
                       : location;
}

private Location LoadAssociation(Location location)
{
   // test derived-type, e.g:
   var state = location as State;

   if (state != null) 
      return _repository.Find().OfType<State>().Include("Country").WithId(id).SingleOrDefault();
}

基本上,我正在打 2 个相同的电话。它有效吗?是的。漂亮吗?不,这并不是真正的“急切加载”。

我知道这不是正确的解决方案,你们能想到正确的解决方案吗? (是的,我知道我可以使用存储过程,但我真的很想在这里查看我的存储库/模型,以便实体正确地附加到图表上,准备好进行编辑)。

即使.Include 导致左外连接,问题是我正在处理“位置”实体集。我需要在“State”上.Include,但“States”属于“Locations”实体集(派生类属于其父实体集)。

所以我想我的问题实际上很笼统 - 当我们事先不知道孩子是什么时,我们如何对抽象实体的孩子执行 .Include?​​strong>

请记住,我不能先使用.OfType&lt;T&gt;()(然后是派生类型上的 .Include),因为我不知道 T 是什么(调用代码也不知道),因此这里不能使用泛型。

【问题讨论】:

    标签: c# linq-to-entities entity-framework-4 abstract-class eager-loading


    【解决方案1】:

    这里真正的问题是您持有Id,但您不知道它代表什么:它可能是CountryState。有时您可能确实知道它是什么,但您没有保留该信息。

    从存储库加载Location 后,您如何知道将其转换为哪种类型才能访问其上的相关关系属性?大概你必须使用 asis 与演员,然后你可以访问这些属性。再一次闻起来有点难闻。

    这里最好的选择是同时维护 Location 对象的 TypeId,以便您可以使用适当的存储库方法重新加载它。

    另一种选择是将关系向上移动到Location 类,这样每个Location 对象都有一个.Parent Location 和一个.Children Locations 集合。现在您可以将它们包含在您的Include 中,当您发现有State 时,您知道查看.Parent,当您有Country 时,您知道查看.Children。使用 null 表示没有父级,使用空集合表示没有子级。现在,当您将大陆或城市添加到 Location 类中时,您将可以很好地使用相同的模型。

    在这种情况下有时可以使用的最后一个选项是在将两个查询转换为通用基本类型后合并两个查询,例如类似:-

    context.Locations.OfType<Country>().Include("States").Cast<Location>().Union(context.Locations.OfType<State>().Include("Countries).Cast<Location>());
    

    【讨论】:

    • 感谢您的回答。 “ID”只是一个例子。实际上,我正在尝试根据唯一的 URI 检索位置(想想登录页面)。所以基本上我只有一个 URI,我需要获取有关该“位置”的信息。我不确定我是否可以将关系提升到 Location 类。这些导航由“状态”表(Sql Server)上的 FK 物理表示。 FK如何拥有自己的FK?是的,UNION 是另一种选择,但成本很高,这些表很大。不过谢谢你的回答,会考虑一下的。
    • 在 SQL 术语中,您将为与 ParentIdChildId 的关联创建一个表,并为每个返回到 Location 表的 FK 创建 FK。如果您在 EF 设计器中进行此关联,它将为您创建一个类似的表。顺便说一句,如果您对性能有足够的担忧而想要急切加载,您可能应该转移到 TPH 并使用鉴别器列将 State 和 Country 存储在同一个表中。否则,所有这些连接都会影响性能。
    • 是的,我们有很多方法可以做到这一点。问题是我们正在处理遗留数据库。在这个阶段我们只是在做 TDD,当我们知道 UI 需要什么时,我们会重新考虑我们的设计。目前,这是一个很好的答案,有很多选择。 +1 并接受,干杯。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多