using System.Reflection;
using AutoMapper;
using MongoDB.Bson;
namespace Dpz.Core.Entity.Base.MapperConfig;
public class GlobalConfigMapper
{
private static readonly Lazy<GlobalConfigMapper> LazyMapper = new(() =>
new GlobalConfigMapper()
);
private GlobalConfigMapper()
{
Mapper = ConfigGlobalMapper();
}
public IMapper Mapper { get; }
public static GlobalConfigMapper GetInstance()
{
return LazyMapper.Value;
}
public static GlobalConfigMapper Instance => LazyMapper.Value;
private static IMapper ConfigGlobalMapper()
{
var config = new MapperConfigurationExpression();
// 获取所有需要扫描的程序集
var assemblies = GetRelevantAssemblies();
// 使用 AutoMapper 内置扫描 (最优雅的方式)
// 自动扫描所有实现了 Profile 的类
// config.AddMaps(assemblies);
// 1. 扫描所有程序集中的类型
var allTypes = assemblies.SelectMany(a => a.GetExportedTypes()).ToArray();
// 2. 基于 IMapFrom<T> 接口,默认规则映射
ConfigureIMapFromMappings(allTypes, config);
// 3. 基于 IHaveCustomMapping 接口,自定义规则映射
ConfigureStaticInterfaceMappings(allTypes, config);
// 4. 基于特性的映射
ConfigureAttributeBasedMappings(allTypes, config);
// 5. 内置映射规则
ConfigureBuiltInMappings(config);
var cfg = new MapperConfiguration(config);
IMapper mapper = new Mapper(cfg);
return mapper;
}
/// <summary>
/// 获取所有相关的程序集
/// 只扫描项目相关的程序集,避免扫描系统程序集提升性能
/// </summary>
private static Assembly[] GetRelevantAssemblies()
{
var assemblies = AppDomain
.CurrentDomain.GetAssemblies()
.Where(a =>
{
var name = a.GetName().Name;
// 只扫描项目相关的程序集
return name != null
&& name.StartsWith("Dpz.Core", StringComparison.OrdinalIgnoreCase);
})
.Where(a => !a.IsDynamic)
.ToArray();
return assemblies;
}
/// <summary>
/// 配置 IMapFrom 接口映射
/// </summary>
private static void ConfigureIMapFromMappings(
Type[] types,
MapperConfigurationExpression config
)
{
var mapTypes =
from x in types
from y in x.GetInterfaces()
let faceType = y.GetTypeInfo()
where
faceType.IsGenericType
&& y.GetGenericTypeDefinition() == typeof(IMapFrom<>)
&& !x.IsAbstract
&& !x.IsInterface
select new { Source = y.GetGenericArguments()[0], Destination = x };
foreach (var item in mapTypes)
{
config.CreateMap(item.Source, item.Destination);
config.CreateMap(item.Destination, item.Source);
}
}
/// <summary>
/// 配置静态接口映射(C# 11+)
/// </summary>
private static void ConfigureStaticInterfaceMappings(
Type[] types,
MapperConfigurationExpression config
)
{
var customMappingTypes = types.Where(x =>
x.GetInterfaces().Any(i => i == typeof(IHaveCustomMapping))
&& x is { IsAbstract: false, IsInterface: false }
);
foreach (var type in customMappingTypes)
{
// 调用静态 CreateMappings 方法
var method = type.GetMethod(
nameof(IHaveCustomMapping.CreateMappings),
BindingFlags.Static | BindingFlags.Public,
null,
[typeof(MapperConfigurationExpression)],
null
);
method?.Invoke(null, [config]);
}
}
/// <summary>
/// 配置基于特性的映射
/// </summary>
private static void ConfigureAttributeBasedMappings(
Type[] types,
MapperConfigurationExpression config
)
{
foreach (var type in types)
{
var mappingAttributes = type.GetCustomAttributes<AutoMappingAttribute>();
foreach (var attr in mappingAttributes)
{
attr.ConfigureMapping(config);
}
}
}
/// <summary>
/// 配置内置映射规则
/// </summary>
private static void ConfigureBuiltInMappings(MapperConfigurationExpression config)
{
config
.CreateMap<string, ObjectId>()
.ConstructUsing(
(x, _) =>
!string.IsNullOrEmpty(x) && ObjectId.TryParse(x, out var oid)
? oid
: ObjectId.Empty
);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
GlobalConfigMapper 代码解析
这段代码实现了一个全局的 AutoMapper 配置管理器,用于集中管理和配置应用程序中的所有对象映射关系。以下是其主要功能和实现细节:
主要功能
单例模式实现:
- 使用
Lazy<T>实现线程安全的单例模式 - 提供
GetInstance()和Instance两种方式获取单例
- 使用
自动映射配置:
- 扫描指定程序集中的所有类型
- 支持多种映射配置方式:
- 基于
IMapFrom<T>接口的自动双向映射 - 基于
IHaveCustomMapping接口的自定义映射规则 - 基于
AutoMappingAttribute特性的映射配置 - 内置的特殊类型映射规则
- 基于
性能优化:
- 只扫描项目相关的程序集(以"Dpz.Core"开头)
- 避免扫描系统程序集和动态程序集
关键实现细节
程序集扫描:
GetRelevantAssemblies()方法筛选出项目相关的程序集- 只处理非动态程序集
多种映射配置策略:
ConfigureIMapFromMappings: 处理实现了IMapFrom<T>接口的类型,自动创建双向映射ConfigureStaticInterfaceMappings: 处理实现了IHaveCustomMapping接口的类型,调用其静态CreateMappings方法ConfigureAttributeBasedMappings: 处理带有AutoMappingAttribute特性的类型ConfigureBuiltInMappings: 配置内置的特殊类型映射,如字符串到 MongoDB ObjectId 的转换
初始化流程:
- 构造函数中调用
ConfigGlobalMapper()初始化映射配置 - 创建
MapperConfiguration并最终生成IMapper实例
- 构造函数中调用
使用场景
这段代码适合在大型项目中集中管理对象映射关系,特别是当:
- 项目使用领域驱动设计(DDD)
- 需要处理大量DTO与领域模型之间的转换
- 使用MongoDB等NoSQL数据库(内置了ObjectId转换)
- 需要统一管理各种自定义映射规则
通过这种方式,可以避免分散的映射配置,提高代码的可维护性和一致性。
评论加载中...