如何在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
相关产品推荐
相关产品推荐

