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 配置管理器,用于集中管理和配置应用程序中的所有对象映射关系。以下是其主要功能和实现细节:

主要功能

  1. 单例模式实现

    • 使用 Lazy<T> 实现线程安全的单例模式
    • 提供 GetInstance()Instance 两种方式获取单例
  2. 自动映射配置

    • 扫描指定程序集中的所有类型
    • 支持多种映射配置方式:
      • 基于 IMapFrom<T> 接口的自动双向映射
      • 基于 IHaveCustomMapping 接口的自定义映射规则
      • 基于 AutoMappingAttribute 特性的映射配置
      • 内置的特殊类型映射规则
  3. 性能优化

    • 只扫描项目相关的程序集(以"Dpz.Core"开头)
    • 避免扫描系统程序集和动态程序集

关键实现细节

  1. 程序集扫描

    • GetRelevantAssemblies() 方法筛选出项目相关的程序集
    • 只处理非动态程序集
  2. 多种映射配置策略

    • ConfigureIMapFromMappings: 处理实现了 IMapFrom<T> 接口的类型,自动创建双向映射
    • ConfigureStaticInterfaceMappings: 处理实现了 IHaveCustomMapping 接口的类型,调用其静态 CreateMappings 方法
    • ConfigureAttributeBasedMappings: 处理带有 AutoMappingAttribute 特性的类型
    • ConfigureBuiltInMappings: 配置内置的特殊类型映射,如字符串到 MongoDB ObjectId 的转换
  3. 初始化流程

    • 构造函数中调用 ConfigGlobalMapper() 初始化映射配置
    • 创建 MapperConfiguration 并最终生成 IMapper 实例

使用场景

这段代码适合在大型项目中集中管理对象映射关系,特别是当:

  • 项目使用领域驱动设计(DDD)
  • 需要处理大量DTO与领域模型之间的转换
  • 使用MongoDB等NoSQL数据库(内置了ObjectId转换)
  • 需要统一管理各种自定义映射规则

通过这种方式,可以避免分散的映射配置,提高代码的可维护性和一致性。

评论加载中...