首页 新闻 会员 周边

LINQ 中重用 Expression<Func<Blog, bool>> 的问题

0
悬赏园豆:30 [已解决问题] 解决于 2026-03-03 10:45

在下面的 C# 代码中针对 Blog 实体定义了 Expression<Func<Blog, bool>> (PublicSpec 方法部分)

public static class BlogsSpecs
{
    extension(IQueryable<Blog> blogs)
    {
        public IQueryable<Blog> Public()
            => blogs.Where(PublicSpec());
    }

    public static Expression<Func<Blog, bool>> PublicSpec()
        => b => b.Flag.HasFlag(ConfigurationFlag.IsPrivated) == false;
}

请问在 BlogPost 的导航属性 Blog 中如何使用这个表达式?

public static class BlogPostSpecs
{
    extension(IQueryable<BlogPost> posts)
    {
        public IQueryable<BlogPost> BlogIsPublic()
          => posts.Where(p => p.BlogSite); // 如何调用 BlogsSpecs.PublicSpec()
    }
}
dudu的主页 dudu | 高人七级 | 园豆:23394
提问于:2026-03-02 15:51
< >
分享
最佳答案
0

通过 deepseek 的回答解决了,需要将 p => p.BlogSitePublicSpec() 组合成一个新的表达式才行

public IQueryable<BlogPost> BlogIsPublic()
{
    Expression<Func<BlogPost, Blog>> blogSelector = p => p.Blog;
    var postFilter = ExpressionCombiner.Compose(BlogsSpecs.PublicSpec(), blogSelector);
    return posts.Where(postFilter);
}

ExpressionCombiner 的实现如下

public static class ExpressionCombiner
{
    public static Expression<Func<T, bool>> Compose<T, TProp>(
        Expression<Func<TProp, bool>> predicate,
        Expression<Func<T, TProp>> selector)
    {
        var param = selector.Parameters[0];
        var body = new ParameterReplacer(selector.Body).Visit(predicate.Body);
        return Expression.Lambda<Func<T, bool>>(body, param);
    }

    private class ParameterReplacer(Expression replacement) : ExpressionVisitor
    {
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return replacement;
        }
    }
}
dudu | 高人七级 |园豆:23394 | 2026-03-02 22:52

后续改进中实现了一个 For 扩展方法

public static class ExpresstionExtensions
{
    extension<TProp>(Expression<Func<TProp, bool>> predicate)
    {
        public Expression<Func<T, bool>> For<T>(Expression<Func<T, TProp>> selector)
        {
            return Compose(predicate, selector);
        }
    }

    public static Expression<Func<T, bool>> Compose<T, TProp>(
        Expression<Func<TProp, bool>> predicate,
        Expression<Func<T, TProp>> selector)
    {
        var param = selector.Parameters[0];
        var body = new ParameterReplacer(selector.Body).Visit(predicate.Body);
        return Expression.Lambda<Func<T, bool>>(body, param);
    }

    private class ParameterReplacer(Expression replacement) : ExpressionVisitor
    {
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return replacement;
        }
    }
}

调用扩展方法的代码

public IQueryable<BlogPost> BlogIsPublic()
{
    return posts.Where(BlogsSpecs.PublicSpec().For<Bloge, BlogPost>(p => p.Blog));
}
dudu | 园豆:23394 (高人七级) | 2026-03-03 10:44
其他回答(1)
0

public static class BlogPostSpecs
{
public static IQueryable<BlogPost> BlogIsPublic(this IQueryable<BlogPost> posts)
{
return posts.Where(p => BlogsSpecs.PublicSpec().Invoke(p.Blog));
}
}

收获园豆:30
mlhello-world | 园豆:236 (菜鸟二级) | 2026-03-02 17:20

PublicSpec 没有 Invoke 方法,需要先调用 Compile 方法

public IQueryable<BlogPost> BlogIsPublic()
    => posts.Where(p => BlogsSpecs.PublicSpec().Compile().Invoke(p.Blog));

但这样不可行,EF Core 翻译不了,会报错

'.Where(b => Func<Blog, bool>.Invoke(b.Inner))' could not be translated.
支持(0) 反对(0) dudu | 园豆:23394 (高人七级) | 2026-03-02 17:49

不好意思,之前弄错了,不加 Complile,可以直接 Invoke ,但 EF Core 同样翻译不了,报错信息如下

The LINQ expression 'b => b.Flag.HasFlag((Enum)IsPrivated) == False' could not be translated
支持(0) 反对(0) dudu | 园豆:23394 (高人七级) | 2026-03-02 23:27
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册