问题
To emulate Lazy Loading, I would like to have a method that recursively Include's the complete object graph via Eager Loading so that upon loading the entity, all of its related data is loaded as well.
From MSDN documentation:
- To include a single reference: query.Include(e => e.Level1Reference).
- To include a single collection: query.Include(e => e.Level1Collection).
- To include a reference and then a reference one level down: query.Include(e => e.Level1Reference.Level2Reference).
- To include a reference and then a collection one level down: query.Include(e => e.Level1Reference.Level2Collection).
- To include a collection and then a reference one level down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference)).
- To include a collection and then a collection one level down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
- To include a collection and then a reference one level down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference)).
- To include a collection and then a collection one level down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
- To include a collection, a reference, and a reference two levels down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference.Level3Reference)).
- To include a collection, a collection, and a reference two levels down: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection.Select(l2 => l2.Level3Reference))).
Question:
How can I recursively Include ALL navigable properties and build this into a generic repository method so that I can get a deep object graph of an entity whenever required, regardless if new properties were added?
回答1:
Ok, this is an edited version that should meet your requirements a little better:
private static void EnumerateAllIncludesList(DbContext context, IEnumerable entities, List<object> entitiesLoaded = null)
{
if (entitiesLoaded == null)
entitiesLoaded = new List<object>();
foreach (var entity in entities)
EnumerateAllIncludesEntity(context, entity, entitiesLoaded);
}
private static void EnumerateAllIncludesEntity(DbContext context, object entity, List<object> entitiesLoaded)
{
if (entitiesLoaded.Contains(entity))
return;
entitiesLoaded.Add(entity);
Type type = entity.GetType();
var properties = type.GetProperties();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
bool isCollection = propertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)) &&
!propertyType.Equals(typeof(string));
if (isCollection)
{
var entry = context.Entry(entity);
if(entry.Member(propertyInfo.Name) as DbCollectionEntry == null)
continue;
entry.Collection(propertyInfo.Name).Load();
var propertyValue = propertyInfo.GetValue(entity);
if (propertyValue == null)
continue;
EnumerateAllIncludesList(context, (IEnumerable)propertyValue, entitiesLoaded);
}
else if ((!propertyType.IsValueType && !propertyType.Equals(typeof(string))))
{
var entry = context.Entry(entity);
if (entry.Member(propertyInfo.Name) as DbReferenceEntry == null)
continue;
entry.Reference(propertyInfo.Name).Load();
var propertyValue = propertyInfo.GetValue(entity);
if (propertyValue == null)
continue;
EnumerateAllIncludesEntity(context, propertyValue, entitiesLoaded);
}
else
continue;
}
}
You would use this like so:
using (var context = new MyContext())
{
var result = context.People.Where(x => x.Id == 1).ToList();
EnumerateAllIncludesList(context,result);
}
回答2:
If you use model-first or database-first, you could write some T4 template to generate what you need using edmx model. It is not easy, but possible.
来源:https://stackoverflow.com/questions/15558334/how-can-i-recursively-include-all-navigable-properties-to-emulate-lazy-loading