将 C# 表达式转换为另一个要在 EF 查询中使用的表达式

Transform a C# Expression into another Expression to be used in EF Query

在 .NET Core 应用程序中,我有如下一对多关系。

public class ModelBase
{
    public virtual long Id { get; set; }

    public virtual bool IsDeleted { get; set; }
}

public class Foo : ModelBase
{
    public virtual string Name { get; set; }

    public virtual ICollection<Bar> Bars { get; set; }
}

public class Bar : ModelBase
{
    public virtual string Name { get; set; }

    public virtual long FooId { get; set; }

    public virtual Foo Foo { get; set; }
}

使用 EF 3.1,我需要查询 IQueryable<Bar> barQuery,其中相应的“Foo”在逻辑上没有被删除。 由于此应用程序不使用延迟加载,我必须执行包含以在单个数据库调用中获取任何 Foo 的 属性。所以我正在尝试实现一个扩展方法,它使用单个表达式同时调用 .Include(x => x.Foo).Where(x => !x.Foo.IsDeleted)

到目前为止,我实现了以下扩展方法:

public static IQueryable<TEntity> IncludeNotDeleted<TEntity, TProperty>(this IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> navigationPropertyPath) 
        where TEntity : class 
        where TProperty : ModelBase
    {
        return query.Include(navigationPropertyPath).Where(/*Use the navigationPropertyPath to filter by IsDeleted*/);
    }

有什么方法可以将“navigationPropertyPath”转换为另一个要在 .Where() 子句中使用的表达式。或者至少通过另一个单一的扩展方法解决方案接近这个相同的目标?

那我可以打电话给barQuery.IncludeNotDeleted(x => x.Foo)

使用以下方法生成修改后的Expression:

private static Expression<Func<TEntity, bool>> GeneratePredicate<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> src)
    where TEntity : class
    where TProperty : ModelBase
{
    var isDeletedPropExpr = src.Body.MakeMemberAccess(typeof(ModelBase).GetMember(nameof(ModelBase.IsDeleted)).First());
    var newBody = Expression.Equal(isDeletedPropExpr, Expression.Constant(false));
    
    return Expression.Lambda<Func<TEntity, bool>>(newBody, src.Parameters[0]);
}

然后将其集成到您的扩展方法中:

public static IQueryable<TEntity> IncludeNotDeleted<TEntity, TProperty>(this IQueryable<TEntity> query, Expression<Func<TEntity, TProperty>> navigationPropertyPath)
        where TEntity : class
        where TProperty : ModelBase
{
    var predicate = GeneratePredicate(navigationPropertyPath);
    return query.Include(navigationPropertyPath).Where(predicate);
}

上面使用了EF Core提供的MakeMemberAccess()辅助方法