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

如何在C#中实现通用的类剩余属性映射方法?

通用剩余属性映射方法实现方案

问题背景

我正在开发一项任务,需调用数据库获取数据并映射到类对象。当前使用AutoMapper将数据库数据映射至以下C#类:

public class PolicyRequest
{
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Landline { get; set; }
    public string Mobile { get; set; }
    public string Email { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    public string Address4 { get; set; }
    public string Postcode { get; set; }
    public string AccountName { get; set; }
    public string AccountNumber { get; set; }
    public string SortCode { get; set; }
    public int PaymentDay { get; set; }
    public string Reference { get; set; }
    public Policy Policy { get; set; }
    public string PreviousReference { get; set; }
}

public class Policy
{
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Landline { get; set; }
    public string Mobile { get; set; }
    public string Email { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    public string Address4 { get; set; }
    public string Postcode { get; set; }
    public string AccountName { get; set; }
    public string AccountNumber { get; set; }
}

初始映射后,Policy对象和PreviousReference属性未完成映射,需手动补全。目前手动补全的代码如下:

request.PreviousReference = request.PreviousReference;
request.Policy = new Policy
{
    Title = request.Title,
    Forename = request.Forename,
    Surname = request.Surname,
    DateOfBirth = request.DateOfBirth,
    Landline = request.Landline,
    Mobile = request.Mobile,
    Email = request.Email,
    Address1 = (request.Address1 ?? string.Empty).Trim(),
    Address2 = (request.Address2 ?? string.Empty).Trim(),
    Address3 = (request.Address3 ?? string.Empty).Trim(),
    Address4 = (request.Address4 ?? string.Empty).Trim(),
}

尝试编写MapRemainings方法但无法实现通用性,请问如何实现通用的剩余属性映射方法?另外该方法是否应设为void类型?


解决方案

方案一:用AutoMapper完成完整映射(推荐)

没必要手动编写映射逻辑,AutoMapper本身支持嵌套对象映射和自定义规则,直接配置映射即可彻底避免重复代码。

配置示例:

var config = new MapperConfiguration(cfg =>
{
    // 如果是从数据库实体映射到PolicyRequest,将第一个泛型参数换成你的DB实体类型
    cfg.CreateMap<PolicyRequest, PolicyRequest>()
       .ForMember(dest => dest.PreviousReference, opt => opt.MapFrom(src => src.PreviousReference))
       .ForMember(dest => dest.Policy, opt => opt.MapFrom(src => new Policy
        {
            Title = src.Title,
            Forename = src.Forename,
            Surname = src.Surname,
            DateOfBirth = src.DateOfBirth,
            Landline = src.Landline,
            Mobile = src.Mobile,
            Email = src.Email,
            Address1 = (src.Address1 ?? string.Empty).Trim(),
            Address2 = (src.Address2 ?? string.Empty).Trim(),
            Address3 = (src.Address3 ?? string.Empty).Trim(),
            Address4 = (src.Address4 ?? string.Empty).Trim(),
            Postcode = src.Postcode,
            AccountName = src.AccountName,
            AccountNumber = src.AccountNumber
        }));
});

var mapper = config.CreateMapper();
// 使用时直接执行映射
var mappedRequest = mapper.Map<PolicyRequest>(sourceRequest);

方案二:通用反射映射方法

如果必须手动实现通用映射,可以利用反射复制同名属性,同时支持自定义特殊规则(比如地址的空值处理和Trim):

// 通用映射方法:复制源对象的同名属性到目标对象,支持自定义映射规则
public static void MapRemainingProperties<TSource, TTarget>(TSource source, TTarget target, Dictionary<string, Action<TSource, TTarget>> customMappings = null)
{
    if (source == null || target == null)
        return;

    var sourceProps = typeof(TSource).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
    var targetProps = typeof(TTarget).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

    // 复制同名同类型属性
    foreach (var sourceProp in sourceProps)
    {
        var targetProp = targetProps.FirstOrDefault(p => p.Name == sourceProp.Name && p.PropertyType == sourceProp.PropertyType);
        if (targetProp != null && targetProp.CanWrite)
        {
            var value = sourceProp.GetValue(source);
            targetProp.SetValue(target, value);
        }
    }

    // 应用自定义映射规则
    if (customMappings != null)
    {
        foreach (var mapping in customMappings)
        {
            mapping.Value(source, target);
        }
    }
}

// 使用示例:补全PolicyRequest的剩余映射
public void CompletePolicyRequestMapping(PolicyRequest request)
{
    // 处理PreviousReference(根据实际数据源调整,这里示例从自身复制)
    request.PreviousReference = request.PreviousReference;

    // 初始化Policy并执行映射
    request.Policy = new Policy();
    var customAddressRules = new Dictionary<string, Action<PolicyRequest, Policy>>
    {
        { nameof(Policy.Address1), (src, dest) => dest.Address1 = (src.Address1 ?? string.Empty).Trim() },
        { nameof(Policy.Address2), (src, dest) => dest.Address2 = (src.Address2 ?? string.Empty).Trim() },
        { nameof(Policy.Address3), (src, dest) => dest.Address3 = (src.Address3 ?? string.Empty).Trim() },
        { nameof(Policy.Address4), (src, dest) => dest.Address4 = (src.Address4 ?? string.Empty).Trim() }
    };

    MapRemainingProperties(request, request.Policy, customAddressRules);
}

关于方法返回类型的选择

  • 如果方法是直接修改传入的引用类型对象(比如上面的MapRemainingProperties),用void完全合理,因为引用类型的修改会直接反映到原实例上。
  • 如果希望方法返回新的映射后的对象(无副作用,更符合函数式风格),可以改成返回TTarget类型:
public static TTarget MapRemainingProperties<TSource, TTarget>(TSource source, TTarget target = null, Dictionary<string, Action<TSource, TTarget>> customMappings = null)
{
    target = target ?? Activator.CreateInstance<TTarget>();
    // 执行上述映射逻辑...
    return target;
}

// 使用方式
request.Policy = MapRemainingProperties(request, customMappings: customAddressRules);

具体选择哪种类型,取决于你的业务场景和代码风格偏好。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:53:11