网站首页 网站源码

本文档提供了几种优雅的自动映射类型实现方案,解决了原始 ServiceMapper 不支持有参数构造函数的问题。
原始的 ServiceMapper 使用 Activator.CreateInstance(x) 来创建实例,这种方式:
适用场景: C# 11+ 项目,不需要依赖注入
public class ProductViewModel : IHaveStaticMapping
{
public string Name { get; set; }
public decimal Price { get; set; }
public static void CreateMappings(MapperConfigurationExpression cfg)
{
cfg.CreateMap<ProductEntity, ProductViewModel>();
cfg.CreateMap<ProductViewModel, ProductEntity>();
}
}
优点:
缺点:
适用场景: 简单映射关系,声明式配置
[AutoMap(typeof(UserEntity), typeof(UserViewModel))]
[CustomMap(typeof(UserEntity), typeof(UserDetailViewModel), nameof(ConfigureUserDetailMapping))]
public class UserViewModel : IMapFrom<UserEntity>
{
public string Name { get; set; }
public string Email { get; set; }
public static void ConfigureUserDetailMapping(MapperConfigurationExpression cfg)
{
cfg.CreateMap<UserEntity, UserDetailViewModel>()
.ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
}
}
优点:
缺点:
适用场景: 现代项目,需要依赖注入,复杂映射逻辑
public class OrderMappingProfile : MappingProfile
{
private readonly IDateTimeService _dateTimeService;
public OrderMappingProfile(IDateTimeService dateTimeService)
{
_dateTimeService = dateTimeService;
CreateMappings();
}
// 备用无参构造函数
public OrderMappingProfile() : this(new DefaultDateTimeService()) { }
private void CreateMappings()
{
CreateMap<OrderEntity, OrderViewModel>()
.ForMember(dest => dest.OrderDate,
opt => opt.MapFrom(src => _dateTimeService.ToLocalTime(src.CreatedAt)));
}
}
优点:
缺点:
适用场景: 兼容现有代码,渐进式升级
// 在 ImprovedServiceMapper 中使用
private object? CreateInstanceWithFactory(Type type)
{
// 1. 尝试无参构造函数
try { return Activator.CreateInstance(type); }
catch { /* 继续尝试其他方法 */ }
// 2. 尝试最少参数的构造函数
var constructors = type.GetConstructors().OrderBy(c => c.GetParameters().Length);
foreach (var constructor in constructors)
{
try
{
var args = constructor.GetParameters()
.Select(p => GetDefaultValue(p.ParameterType))
.ToArray();
return Activator.CreateInstance(type, args);
}
catch { /* 尝试下一个构造函数 */ }
}
// 3. 使用 FormatterServices(不调用构造函数)
return FormatterServices.GetUninitializedObject(type);
}
ImprovedServiceMapper 替换现有的 ServiceMapper// 在 Startup.cs 或 Program.cs 中
services.AddAutoMapper(); // 使用扩展方法自动注册
// 或手动注册
services.AddSingleton<DependencyInjectionServiceMapper>();
services.AddSingleton<IMapper>(provider =>
provider.GetRequiredService<DependencyInjectionServiceMapper>().Mapper);
// 注册映射配置文件
services.AddTransient<OrderMappingProfile>();
services.AddTransient<UserMappingProfile>();
| 方案 | 初始化性能 | 运行时性能 | 内存占用 |
|---|---|---|---|
| 静态接口 | 最优 | 最优 | 最少 |
| 特性配置 | 优秀 | 优秀 | 少 |
| 配置文件 | 良好 | 优秀 | 中等 |
| 智能工厂 | 一般 | 优秀 | 中等 |
推荐使用映射配置文件方案,它提供了最好的灵活性和可维护性。对于简单场景,静态接口映射也是不错的选择。智能工厂方案可以作为过渡方案,帮助现有代码平滑升级。
