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

如何重构多数据集后端代码以降低维护成本?

代码重构方案:减少数据集Command/Query重复代码

针对你提到的40个数据集表单重复创建Command/Query文件、维护成本高的问题,以下是几个实用的重构方案,按落地优先级排序:

1. 分组抽象+泛型模板方法

既然全局基类因字段差异不适用,那就按数据集组(比如IDCards组)做分层抽象,结合泛型复用通用逻辑:

实现步骤

  • 先按业务组定义组级基类,把组内数据集共有的字段、验证逻辑放在这里:
    // IDCards组的基类,包含组内通用字段
    public abstract class BaseIDCardCommand<TEntity> : BaseCreateCommand<TEntity>
        where TEntity : class, new()
    {
        public int CountryID { get; set; } // 组内共用字段
        public string IDNumber { get; set; }
    }
    
  • 再定义全局泛型基类,封装所有数据集通用的CRUD逻辑(比如持久化、基础验证):
    public abstract class BaseCreateCommand<TEntity> : IRequest<int>
        where TEntity : class, new()
    {
        public string CreatedBy { get; set; }
        public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
    
        // 模板方法:子类实现字段到实体的映射
        public abstract TEntity MapToEntity();
    
        // 通用验证逻辑
        public virtual async Task<ValidationResult> ValidateAsync(ValidationContext context)
        {
            var result = new ValidationResult();
            if (string.IsNullOrEmpty(CreatedBy))
            {
                result.Errors.Add(new ValidationFailure(nameof(CreatedBy), "创建人不能为空"));
            }
            return result;
        }
    }
    
  • 每个数据集的Command只需要继承对应组的基类,实现专属字段和逻辑:
    public class CreateValidIDCardsCommand : BaseIDCardCommand<ValidIDCards>
    {
        // 专属字段:比如本国身份证特有的户籍地址
        public string HouseholdAddress { get; set; }
    
        public override ValidIDCards MapToEntity()
        {
            return new ValidIDCards
            {
                CountryID = CountryID,
                IDNumber = IDNumber,
                HouseholdAddress = HouseholdAddress,
                CreatedBy = CreatedBy,
                CreatedTime = CreatedTime
            };
        }
    
        // 专属验证:比如户籍地址格式检查
        public override async Task<ValidationResult> ValidateAsync(ValidationContext context)
        {
            var baseResult = await base.ValidateAsync(context);
            if (string.IsNullOrEmpty(HouseholdAddress))
            {
                baseResult.Errors.Add(new ValidationFailure(nameof(HouseholdAddress), "户籍地址不能为空"));
            }
            return baseResult;
        }
    }
    

优点

  • 类型安全,编译时就能发现错误;
  • 修改组内共用字段(比如CountryID类型)时,只需要修改组基类,不用改所有子类;
  • 保留每个数据集的专属逻辑灵活性。

2. 代码生成器(T4模板/Source Generator)

如果新增数据集的重复工作占比高,直接用代码生成器批量生成Command、Query、Handler文件,从根源上消除重复:

实现思路

  • 定义元数据:可以用数据库表结构、JSON配置文件或者Excel表格,记录每个数据集的名称、字段列表、类型;
  • 用T4模板或.NET Source Generator读取元数据,自动生成全套CRUD代码:
    • T4模板适合手动触发生成,修改方便;
    • Source Generator适合实时生成,编译时自动更新代码。

示例T4模板片段

<#@ template language="C#" #>
<#@ parameter name="EntityName" type="string" #>
<#@ parameter name="CommonFields" type="List<FieldInfo>" #>
<#@ parameter name="UniqueFields" type="List<FieldInfo>" #>

// 自动生成的Create命令
public class Create<#= EntityName #>Command : IRequest<int>
{
<# foreach(var field in CommonFields) { #>
    public <#= field.Type #> <#= field.Name #> { get; set; }
<# } #>
<# foreach(var field in UniqueFields) { #>
    public <#= field.Type #> <#= field.Name #> { get; set; }
<# } #>
}

// 自动生成的Create命令Handler
public class Create<#= EntityName #>CommandHandler : IRequestHandler<Create<#= EntityName #>Command, int>
{
    private readonly AppDbContext _dbContext;

    public Create<#= EntityName #>CommandHandler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<int> Handle(Create<#= EntityName #>Command request, CancellationToken cancellationToken)
    {
        var entity = new <#= EntityName #>
        {
<# foreach(var field in CommonFields.Concat(UniqueFields)) { #>
            <#= field.Name #> = request.<#= field.Name #>,
<# } #>
            CreatedBy = request.CreatedBy,
            CreatedTime = DateTime.UtcNow
        };

        _dbContext.<#= EntityName #>.Add(entity);
        await _dbContext.SaveChangesAsync(cancellationToken);
        return entity.Id;
    }
}

优点

  • 新增数据集只需要更新元数据,一键生成所有代码;
  • 修改字段类型时,更新元数据后重新生成,不用手动修改几十上百个文件;
  • 代码风格统一,避免手动编写的错误。

3. 动态DTO+通用Handler(适合快速迭代场景)

如果对类型安全要求不高,且字段变化频繁,可以用动态对象+反射实现通用的CRUD逻辑,完全避免重复创建Command/Query文件:

实现示例

// 通用创建命令
public class GenericCreateCommand : IRequest<int>
{
    public string EntityName { get; set; } // 数据集实体名称
    public Dictionary<string, object> Fields { get; set; } // 字段键值对
    public string CreatedBy { get; set; }
}

// 通用创建命令Handler
public class GenericCreateCommandHandler : IRequestHandler<GenericCreateCommand, int>
{
    private readonly AppDbContext _dbContext;

    public GenericCreateCommandHandler(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<int> Handle(GenericCreateCommand request, CancellationToken cancellationToken)
    {
        // 通过反射获取实体类型
        var entityType = Assembly.GetExecutingAssembly().GetTypes()
            .First(t => t.Name == request.EntityName);
        var entity = Activator.CreateInstance(entityType);

        // 设置通用字段
        entityType.GetProperty("CreatedBy")?.SetValue(entity, request.CreatedBy);
        entityType.GetProperty("CreatedTime")?.SetValue(entity, DateTime.UtcNow);

        // 设置自定义字段
        foreach (var field in request.Fields)
        {
            var prop = entityType.GetProperty(field.Key);
            if (prop != null)
            {
                var value = Convert.ChangeType(field.Value, prop.PropertyType);
                prop.SetValue(entity, value);
            }
        }

        // 添加到DbSet并保存
        var dbSet = _dbContext.GetType().GetMethod("Set").MakeGenericMethod(entityType).Invoke(_dbContext, null);
        dbSet.GetType().GetMethod("Add").Invoke(dbSet, new[] { entity });

        await _dbContext.SaveChangesAsync(cancellationToken);
        return (int)entityType.GetProperty("Id").GetValue(entity);
    }
}

优缺点

  • 优点:无需为每个数据集编写Command/Handler,新增数据集零代码;
  • 缺点:编译时无类型检查,容易出现字段名称/类型错误,调试难度高,不适合大型生产系统。

实践建议

优先采用分组抽象+代码生成器的组合方案:

  1. 先按业务组定义基类,封装组内通用逻辑;
  2. 用代码生成器基于基类生成每个数据集的专属代码;
  3. 后续修改共用字段时,只需要更新组基类和元数据,重新生成代码即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:58:58