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

迁移ColdFusion至.NET:如何简洁保存含多嵌套类的大型对象?

优化嵌套领域对象批量保存的方案:工厂+策略模式组合

我之前重构遗留系统时也碰到过几乎一模一样的场景——几百个可选字段拆成一堆嵌套子类,事务里挨个写保存逻辑,代码臃肿到后期改起来心惊胆战。结合你提到的建造者/工厂思路,我推荐用工厂模式+策略模式的组合来重构,完美解决这类重复逻辑问题,而且扩展性拉满。

核心思路

把每个子类的保存逻辑封装成独立的「策略」,用工厂根据实体类型匹配对应的策略,最后在事务里统一执行所有策略的保存操作。这样主逻辑里再也不用写15+个if/switch,新增子类只需要加一个策略类就行。

步骤1:定义统一的保存策略接口

先给所有子实体的保存逻辑定个标准:

public interface ISubEntitySaver<T>
{
    Task SaveAsync(T entity, YourDbContext context, CancellationToken cancellationToken = default);
}

这个接口负责封装单个子类的保存逻辑——不管是新增、更新还是关联操作,都放在对应的实现类里。

步骤2:为每个子类实现保存策略

比如你的CustomerInfo子类,就写一个专属的策略类:

public class CustomerInfoSaver : ISubEntitySaver<CustomerInfo>
{
    public async Task SaveAsync(CustomerInfo entity, YourDbContext context, CancellationToken cancellationToken)
    {
        // 这里放原来针对CustomerInfo的保存逻辑:判断是否存在、新增/更新、关联处理等
        if (entity.Id == 0)
        {
            context.CustomerInfos.Add(entity);
        }
        else
        {
            context.CustomerInfos.Update(entity);
        }
        await context.SaveChangesAsync(cancellationToken);
    }
}

把原来散在主方法里的15+段逻辑,逐个搬到对应的策略类里,每个类只负责自己的实体,符合单一职责原则。

步骤3:实现策略工厂

接下来写个工厂,用来根据实体类型获取对应的策略实例。如果用的是ASP.NET Core,可以直接借助DI容器来管理所有策略:

public class SubEntitySaverFactory
{
    private readonly IServiceProvider _serviceProvider;

    public SubEntitySaverFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public ISubEntitySaver<T> GetSaver<T>()
    {
        return _serviceProvider.GetRequiredService<ISubEntitySaver<T>>();
    }
}

记得在Program.cs里注册所有策略:

builder.Services.AddScoped<ISubEntitySaver<CustomerInfo>, CustomerInfoSaver>();
builder.Services.AddScoped<ISubEntitySaver<OrderDetails>, OrderDetailsSaver>();
// 其他13+个子类的策略注册...
builder.Services.AddScoped<SubEntitySaverFactory>();

步骤4:重构主保存逻辑

现在主方法里的代码会变得异常简洁,只需要遍历领域对象的所有子实体,用工厂拿到策略后统一执行:

public async Task SaveLargeDomainObjectAsync(LargeDomainObject domainObject, YourDbContext context, SubEntitySaverFactory saverFactory, CancellationToken cancellationToken)
{
    using var transaction = await context.Database.BeginTransactionAsync(cancellationToken);
    try
    {
        // 遍历所有需要保存的子实体
        if (domainObject.CustomerInfo != null)
        {
            var saver = saverFactory.GetSaver<CustomerInfo>();
            await saver.SaveAsync(domainObject.CustomerInfo, context, cancellationToken);
        }

        if (domainObject.OrderDetails != null)
        {
            var saver = saverFactory.GetSaver<OrderDetails>();
            await saver.SaveAsync(domainObject.OrderDetails, context, cancellationToken);
        }

        // 其他子实体的处理...

        await transaction.CommitAsync(cancellationToken);
    }
    catch (Exception ex)
    {
        await transaction.RollbackAsync(cancellationToken);
        // 异常处理逻辑
        throw;
    }
}

进阶优化:减少手动遍历代码

如果子实体太多,手动写if判断还是有点麻烦,可以用反射+属性标记来自动发现需要保存的子实体:

  1. 自定义一个SaveableSubEntityAttribute,标记在领域对象的子实体属性上;
  2. 用反射遍历领域对象的所有属性,找到带该标记的属性;
  3. 动态调用工厂获取对应的策略并执行保存。

示例代码大概是这样:

// 自定义属性
[AttributeUsage(AttributeTargets.Property)]
public class SaveableSubEntityAttribute : Attribute { }

// 在领域对象里标记属性
public class LargeDomainObject
{
    [SaveableSubEntity]
    public CustomerInfo? CustomerInfo { get; set; }

    [SaveableSubEntity]
    public OrderDetails? OrderDetails { get; set; }
    // ...其他属性
}

// 主方法里用反射自动处理
public async Task SaveLargeDomainObjectAsync(LargeDomainObject domainObject, YourDbContext context, SubEntitySaverFactory saverFactory, CancellationToken cancellationToken)
{
    using var transaction = await context.Database.BeginTransactionAsync(cancellationToken);
    try
    {
        var saveableProperties = typeof(LargeDomainObject)
            .GetProperties()
            .Where(p => p.GetCustomAttribute<SaveableSubEntityAttribute>() != null && p.GetValue(domainObject) != null);

        foreach (var prop in saveableProperties)
        {
            var entityType = prop.PropertyType;
            // 动态调用GetSaver方法
            var saverMethod = typeof(SubEntitySaverFactory).GetMethod(nameof(SubEntitySaverFactory.GetSaver))!.MakeGenericMethod(entityType);
            var saver = saverMethod.Invoke(saverFactory, null);

            // 动态调用SaveAsync方法
            var saveMethod = saver!.GetType().GetMethod(nameof(ISubEntitySaver<object>.SaveAsync))!;
            await (Task)saveMethod.Invoke(saver, new object[] { prop.GetValue(domainObject), context, cancellationToken })!;
        }

        await transaction.CommitAsync(cancellationToken);
    }
    catch (Exception ex)
    {
        await transaction.RollbackAsync(cancellationToken);
        throw;
    }
}

这样后续新增子实体,只需要加属性标记和对应的策略类,主逻辑完全不用改,彻底解决维护痛点。

关于建造者模式的补充

如果你的保存流程需要灵活组合(比如某些场景下不需要保存特定子实体),可以结合建造者模式来构建保存任务:

public class SaveBuilder
{
    private readonly List<Func<Task>> _saveTasks = new();

    public SaveBuilder AddSubEntity<T>(T entity, ISubEntitySaver<T> saver, YourDbContext context, CancellationToken cancellationToken)
    {
        if (entity != null)
        {
            _saveTasks.Add(() => saver.SaveAsync(entity, context, cancellationToken));
        }
        return this;
    }

    public async Task ExecuteAsync(YourDbContext context, CancellationToken cancellationToken)
    {
        using var transaction = await context.Database.BeginTransactionAsync(cancellationToken);
        try
        {
            foreach (var task in _saveTasks)
            {
                await task();
            }
            await transaction.CommitAsync(cancellationToken);
        }
        catch
        {
            await transaction.RollbackAsync(cancellationToken);
            throw;
        }
    }
}

调用的时候就像搭积木一样:

var builder = new SaveBuilder()
    .AddSubEntity(domainObject.CustomerInfo, saverFactory.GetSaver<CustomerInfo>(), context, cancellationToken)
    .AddSubEntity(domainObject.OrderDetails, saverFactory.GetSaver<OrderDetails>(), context, cancellationToken);

await builder.ExecuteAsync(context, cancellationToken);

这种方式更适合保存流程多变的场景,灵活性更强。

总结

用工厂+策略模式重构后,你会发现:

  • 代码复杂度大幅降低,主逻辑从几百行缩成几十行;
  • 每个子类的保存逻辑独立,调试和修改更安全;
  • 新增子类只需要加策略类和注册,完全符合开闭原则;
  • 事务逻辑统一管理,不用重复写try/catch和事务控制。

亲测这种方案在处理15+子类的场景下非常好用,后期维护成本直接砍半!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:40:13