You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在层间对象转换Assembler接口中实现Flatten Expression Tree?

实现Assembler接口中的扁平化表达式树(针对Book/Publisher场景)

搞定这个扁平化表达式树的需求其实不难,我刚好做过类似的对象转换Assembler实现,结合你提到的Book/Publisher场景,给你梳理下具体的实现思路和代码示例:

1. 先理清楚核心需求

咱们的核心痛点是:上层的BookAssembler调用下层PublisherAssembler的GetIncludes方法时,没法提前知道它会返回0个、1个还是N个需要加载的表达式。我们要做的就是把这些嵌套在Publisher层级的表达式,"拉平"到Book层级,和BookAssembler自身的includes合并成一个扁平的集合,方便后续ORM框架直接解析执行。

2. 定义统一的Assembler接口

首先得给所有Assembler定个标准接口,确保GetIncludes方法的返回格式一致:

public interface IAssembler<TSource, TDestination>
{
    // 返回需要从源对象加载的字段/关联对象的表达式树集合
    IEnumerable<Expression<Func<TSource, object>>> GetIncludes();
    // 核心的对象转换方法
    TDestination Assemble(TSource source);
}

举个PublisherAssembler的实现例子,它可能返回任意数量的includes:

public class PublisherAssembler : IAssembler<Publisher, PublisherDto>
{
    public IEnumerable<Expression<Func<Publisher, object>>> GetIncludes()
    {
        // 这里可能返回0个、1个或多个表达式,比如加载Publisher的地址和关联书籍
        yield return p => p.Address;
        yield return p => p.Books;
    }

    public PublisherDto Assemble(Publisher source)
    {
        return new PublisherDto
        {
            Id = source.Id,
            Name = source.Name,
            Address = source.Address?.ToString()
        };
    }
}

3. 核心的扁平化实现逻辑

接下来是BookAssembler的关键部分——把PublisherAssembler返回的表达式树,从Publisher层级转换到Book层级:

public class BookAssembler : IAssembler<Book, BookDto>
{
    private readonly IAssembler<Publisher, PublisherDto> _publisherAssembler;

    public BookAssembler(IAssembler<Publisher, PublisherDto> publisherAssembler)
    {
        _publisherAssembler = publisherAssembler;
    }

    public IEnumerable<Expression<Func<Book, object>>> GetIncludes()
    {
        // 先返回Book自身需要加载的内容,比如作者
        yield return b => b.Author;

        // 遍历PublisherAssembler的includes,逐个扁平化处理
        foreach (var publisherInclude in _publisherAssembler.GetIncludes())
        {
            // 构建参数:Book类型的变量b
            var bookParam = Expression.Parameter(typeof(Book), "b");
            // 获取Book的Publisher属性
            var publisherProp = Expression.Property(bookParam, nameof(Book.Publisher));
            // 把publisherInclude的表达式(p => p.X)转换成b => b.Publisher.X
            var flattenedBody = Expression.Invoke(publisherInclude, publisherProp);
            var flattenedExpr = Expression.Lambda<Func<Book, object>>(flattenedBody, bookParam);
            
            yield return flattenedExpr;
        }
    }

    public BookDto Assemble(Book source)
    {
        return new BookDto
        {
            Id = source.Id,
            Title = source.Title,
            Publisher = _publisherAssembler.Assemble(source.Publisher)
        };
    }
}

4. 优化:封装通用扩展方法

为了避免每个Assembler都重复写扁平化的逻辑,咱们可以把这段逻辑封装成一个通用扩展方法,复用性更强:

public static class AssemblerExtensions
{
    public static IEnumerable<Expression<Func<TSource, object>>> FlattenIncludes<TSource, TChildSource>(
        this IEnumerable<Expression<Func<TChildSource, object>>> childIncludes,
        Expression<Func<TSource, TChildSource>> childPropertySelector)
    {
        var sourceParam = Expression.Parameter(typeof(TSource), "source");
        var childProperty = Expression.Invoke(childPropertySelector, sourceParam);

        foreach (var childInclude in childIncludes)
        {
            var invokedBody = Expression.Invoke(childInclude, childProperty);
            yield return Expression.Lambda<Func<TSource, object>>(invokedBody, sourceParam);
        }
    }
}

这样BookAssembler里的代码就可以简化成:

public IEnumerable<Expression<Func<Book, object>>> GetIncludes()
{
    yield return b => b.Author;

    // 直接调用扩展方法完成扁平化
    foreach (var flattenedInclude in _publisherAssembler.GetIncludes().FlattenIncludes(b => b.Publisher))
    {
        yield return flattenedInclude;
    }
}

这个方案完美适配你说的0~N个includes的场景:如果PublisherAssembler返回空列表,循环直接跳过,不会影响BookAssembler自身的includes;如果返回多个,全部都会被扁平化成Book层级的表达式。

内容的提问来源于stack exchange,提问作者Thomas Lee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:42:15