Entity Framework Core Explicitly loading related data according to Type of Class

.net-core c# entity-framework entity-framework-core

Question

I have Repository class with includes. the code below works for one to one relationships. However for Collections I need to change DbContext.Entry(model.Result).Reference(include).Load(); to DbContext.Entry(model.Result).Collection(include).Load();

    public virtual async Task<TEntity> GetByIdAsync(object[] keyValues,
        List<Expression<Func<TEntity, object>>> includes,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var model = DbSet.FindAsync(keyValues, cancellationToken);
        if (includes == null) return await model;
        foreach (var include in includes)
            //if (include.Body.Type.IsAssignableFrom(typeof(ICollection<>)))
            DbContext.Entry(model.Result).Reference(include).Load();

        return await model;

        //return await DbSet.FindAsync(keyValues, cancellationToken);
    }

What kind of condition may I use here in order to seperate References and Collections from each other?

thank you.

Edit: example object is

System.Collections.Generic.IList`1[[WestCore.Domain.Entities.WestLife.MhpProduct]]

generally, the collection can be ICollection or IList.

1
1
1/18/2018 2:07:40 PM

Accepted Answer

Instead of separate Reference / Collection methods, you can use the more general Navigation method. Unfortunately it has no overload with lambda expressions, so you need to extract the property name manually. Since your method design does not support nested properties, you can get that information from lambda expression body, cast to MemberExpression:

foreach (var include in includes)
{
    var propertyName = ((MemberExpression)include.Body).Member.Name;
    DbContext.Entry(model.Result).Navigation(propertyName).Load();
}
5
1/18/2018 2:40:04 PM

Popular Answer

I'd change it so rather than expressly mimicking FindByIdAsync I'd have a slightly more flexible method:

public async Task<T> GetItemAsync<T>(Expression<Func<T, bool>> filter, 
          List<Expression<Func<T,object>>> includes) where T: class
{
    var model = Set<T>();
    foreach (var include in includes)
    {
        model.Include(include);
    }
    return await model.FirstOrDefaultAsync(filter);
}

which you'd then call with:

var result = GetItemAsync<MyEntity>(x => x.Id == 3, 
             new List<Expression<Func<MyEntity, object>>> 
             { 
                 x => s.SomeProperty, 
                 x => s.SomeOtherProperty
             });

which is very similar to yours, but a bit more flexible, and will only hit the DB once rather than lazily-loading everything separately like I believe your existing code would. This may be your intention but we don't have that context.

Also - Calling model.Result and using the non-asyc Load() method means your code is less async-friendly than it could be...



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow