问题
I have an EF4 Model that is built with abstract entities/classes:
![](https://www.eimg.top/images/2020/03/24/00c6e00210988f617603979e5aed5cbd.png)
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.
回答1:
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>());
来源:https://stackoverflow.com/questions/4011211/entity-framework-4-abstract-model-how-to-programatically-eager-load-navigation