首页 > 解决方案 > 使用 EF Core 查询数据库时,有没有办法有条件地包含或排除导航属性

问题描述

在查询数据库时,我尝试有条件地包含或排除导航属性DbSet<T>,如下所示:

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

如果导航属性为空,我想排除它,但当数据库中有值时包含它。任何达到此目的的尝试都会引发异常:

消息 =“包含属性 lambda 表达式 'i => (i.FeedbackImage ?? null)' 无效。表达式应表示属性访问:'t => t.MyProperty'。要定位在派生类型上声明的导航,请指定的显式类型的 lambda 参数...

当方法接受表达式时,为什么会.Include(i => i.NavigationProp ?? null)失败?Include(Expression<Func<T, T>>)

标签: c#entity-frameworklinq.net-coreef-core-2.2

解决方案


它失败了,因为它Include(Expression<Func<T, T>>)实际上是在Include(string)内部翻译的,最终Include(string)需要一个属性名称,这就是错误告诉你的,这就是实体框架的工作方式。

更新:

当然不是一个优雅的解决方案,但你可以尝试这样的事情:

将导航属性作为接口添加到模型中:

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

模型将实现它:

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

还有一个通用方法,它将检查该接口并在类实现它时执行您的包含:

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

更新 2:

考虑一下,您可以只检查属性名称而不是添加接口:

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

推荐阅读