yaddly
yaddly

Reputation: 360

Is there a way to conditionally include or exclude a navigation property when querying Database using EF Core

I'm attempting to conditionally include or exclude a navigation property when querying Database using DbSet<T> as follows:

var recordList = await dbContext.DbSet<T>
.Include(i => i.NavigationProp ?? null)
.AsNoTracking()
.ToListAsync();

I would like to exclude the Navigation property if it is null but include it when there is a value in the database. Any attempt to achieve this throws exception:

Message = "The Include property lambda expression 'i => (i.FeedbackImage ?? null)' is invalid. The expression should represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, specify an explicitly typed lambda parameter of the....

Why does this .Include(i => i.NavigationProp ?? null) fail when Include(Expression<Func<T, T>>) method accepts an Expression?

Upvotes: 0

Views: 2605

Answers (2)

David Browne - Microsoft
David Browne - Microsoft

Reputation: 89371

I'm trying to check if the Navigation property has a value, if true then it must be included else ignored.

So just

var recordList = await dbContext.DbSet<T>
.Include(i => i.NavigationProp)
.AsNoTracking()
.ToListAsync();

The Navigation Property will be loaded, unless the related Foreign Key value is null, in which case it will be Null.

Upvotes: 1

KiKoS
KiKoS

Reputation: 438

It fails because Include(Expression<Func<T, T>>) gets actually translated to Include(string) internally and eventually Include(string) will need a property name and that's what the error is telling you, that's how entity framework is doing it.

Update:

Certainly not an elegant solution but you could try something like this:

Adding the navigation property as an interface to your models:

public interface IHasNavigationProperty
{
    public NavigationProp NavigationProp { get; set; }
}

Models will implement it:

public class MyModel : IHasNavigationProperty
{
    public NavigationProp NavigationProp { get; set; }
}

And a generic method that will check for that interface and execute the your include if the class implements it:

IList<T> GetRecords<T>() where T : class
{
    var hasNavigationPropertyInterface = typeof(IHasNavigationProperty).IsAssignableFrom(typeof(T));
    var query = _context.Set<T>().AsQueryable();
    if (hasNavigationPropertyInterface)
    {
        var navigationPropertyName = nameof(NavigationProp);
        query = query.Include(navigationPropertyName);
    }
    var recordList = query.AsNoTracking()
        .ToList();
    return recordList;
}

Update 2 :

Thinking about it you can just check for the property name instead of adding an interface:

private IList<T> GetRecords<T>() where T : class
{
    var hasProperty = typeof(T).GetProperty(nameof(NavigationProp)) != null;
    var query = _context.Set<T>().AsQueryable();
    if (hasProperty)
    {
        var navigationPropertyName = nameof(NavigationProp);
        query = query.Include(navigationPropertyName);
    }
    var recordList = query.AsNoTracking()
        .ToList();
    return recordList;
}

Upvotes: 1

Related Questions