实体框架4抽象模型 - 如何编程地加载导航属性? [英] Entity Framework 4 Abstract Model - How to Programatically Eager-Load Navigational Properties?

查看:226
本文介绍了实体框架4抽象模型 - 如何编程地加载导航属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



状态实体具有名为国家/地区的导航属性,alt =alt text>


$ b



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



如果我有以下方法:

  public Location FindSingle(int id)
{
return _repository。查找()WithId(ID).SingleOrDefault()。
}

默认情况下,这不返回任何关联。但是,如果我明确地想要加载关联,那么我如何动态地加载关联?



我不能这样做:

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

由于我正在使用一个名为位置的抽象类,有一个称为国家的航海物业。我不知道导出类型是什么,直到我实际执行查询与 .SingleOrDefault



所以,这是我必须做的:

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

私人位置LoadAssociation(位置位置)
{
//测试派生类型,例如
var state = location as State;

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

本质上,我在做两个相同的电话。它工作吗是。是漂亮吗不,这不是真的渴望加载。



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



即使 .Include 导致左外连接,问题是我正在处理位置实体集。我需要在状态中包含,但国家属于位置实体集(派生类属于其父实体集)。



所以我想我的问题实际上是很通用的 - 我们如何做一个。包含一个抽象实体的孩子,当我们不知道孩子是什么记住,我不能使用 .OfType< T>()然后(然后引用派生类型),因为我不知道T是什么(也不是调用代码),因此泛型不能在这里使用。

解决方案

这里真正的问题是你持有一个 Id 但你不知道它代表什么:它可能是一个 Country State 。在某些时候,你可能确实知道它是什么,但是你没有保留这些信息。



加载位置后从存储库中,您如何知道要将其转换为哪种类型以访问其上的相关关系属性?大概你必须使用作为与转换,然后你可以访问这些属性。再次,这种味道不好。



这里最好的选择是维护 Type Id 的位置对象,以便您可以使用适当的存储库方法重新加载。



另一个选项是替换关系直到位置类,以便每个位置对象具有一个 .Parent 位置和 .Children 位置集合。现在您可以将它们包含在您的 Include 中,当您发现您有状态时,您可以查看 .Parent ,当您有国家/地区时,您可以查看 .Children 。对于没有父项使用null,对于没有子项使用空集合。现在,当您添加大陆或城市到您的地点课程时,您将变得非常棒,以使用相同的模型。



最后一个选项,您有时可以在这是在将它们转换为公共基本类型之后的联合两个查询,例如如下: -

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


I have an EF4 Model that is built with abstract entities/classes:

Notice how State entity has a navigational property called Country.

Note: I have lazy-loading disabled, so i must eager-load on demand.

Now, if i have the following method:

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

This does not return any associations by default. But how can i dynamically eager-load the associations when i explicitly want to?

I cannot do this:

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

As i am working with an abstract class called Location, which does not have a navigational property called "Country". I do not know what the derived type is until i actually execute the query with .SingleOrDefault.

So, here's what i've had to do:

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();
}

Essentially, i'm doing 2 identical calls. Does it work? Yes. Is it pretty? No, and it's not really "eager-loading".

I know this is not the correct solution, can you guys think of the proper one? (and yes i know i can use stored procedures, but i really want to go through my repository/model here, so the entities are correctly attached to the graph, ready for editing).

Even though .Include causes a Left Outer Join, the problem is i'm working off the "Locations" entity set. I need to .Include on the "State", but "States" belong to the "Locations" entity set (derived classes belong to their parent's entity set).

So i guess my question is actually pretty generic - how do we do a .Include on a child of an abstract entity, when we don't know what the child is beforehand?

Remember, i cannot use .OfType<T>() first (and then the .Include on the derived type), as i don't know what T is (and neither does the calling code), hence generics cannot be utilized here.

解决方案

The real issue here is that you are holding an Id but you don't know what it represents: it could be for a Country or for a State. At some time you presumably did know what it was, but you didn't maintain that information.

After loading a Location from the repository, how do you know which type to cast it to in order to access the relevant relationship property on it? Presumably you have to use as or is with cast and then you can access these properties. Again that kinda smells bad.

The best option here would be to maintain both the Type and the Id of a Location object so you can reload it using the appropriate repository method.

Another option would be to instead move the relationship up to the Location class so that every Location object has a .Parent Location and a .Children Locations collection. Now you can include them in your Include and when you find you have a State you know to look at .Parent and when you have a Country you know to look at .Children. Use null for no parent and an empty collection for no children. Now when you add continents or cities to your Location class you'll be in great shape to use the same model.

A final option which you can sometimes use in situations like this is to Union two queries after converting them to the common base type, e.g. something like:-

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

这篇关于实体框架4抽象模型 - 如何编程地加载导航属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆