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

C# Caliburn.Micro SimpleContainer IoC泛型工厂注册问题

问题根因说明

你遇到的MapperBase<LincolnTelemetryModel>无法转换为MapperBase<T>错误,本质是C#泛型的默认不变性导致的:C#泛型在没有显式声明协变/逆变的前提下,哪怕两个类型A和B存在继承关系,Generic<A>和Generic<B>也不存在类型兼容关系,不能直接强制转换。
举个直白的例子:如果调用工厂时把泛型参数T指定为其他类型(比如OtherTelemetryModel),方法要求返回MapperBase<OtherTelemetryModel>,但你实际创建的是LincolnTelemetryMapper,后续调用Map方法传入对应类型参数时会直接触发运行时错误,因此编译器从根源上禁止了这种不安全的转换。
另外你当前的工厂实现还有两个明显问题:

  • switch (mappedObject)的写法不符合C#语法(旧版本C#不支持case后直接跟类型名,且你传入的是对象实例,不是类型本身)
  • 所有Mapper类型硬编码在switch逻辑里,每新增一个Mapper就要修改工厂代码,违反开闭原则,后续维护成本极高
通用无硬编码工厂实现方案

完全不需要为每个实体单独写泛型工厂,也不需要手动注册每个Mapper,核心思路是:

  1. 增加非泛型的基类和工厂接口,规避泛型转换问题
  2. 程序启动时通过反射自动扫描所有MapperBase<>的实现类,缓存「实体类型→对应Mapper类型」的映射关系
  3. 所有Mapper实例交给IoC容器创建,自动完成依赖注入,不需要手动new对象

第一步:调整基类定义

先加一层非泛型的Mapper基类,方便统一存储和调用:

// 非泛型基类,用于工厂统一返回
public abstract class MapperBase
{
    public abstract IEnumerable MapAll(IDataReader reader);
}

// 泛型基类继承非泛型基类
public abstract class MapperBase<T> : MapperBase
{
    protected abstract T Map(IDataRecord record);

    public override IEnumerable MapAll(IDataReader reader)
    {
        return MapAllGeneric(reader);
    }

    public Collection<T> MapAllGeneric(IDataReader reader)
    {
        var result = new Collection<T>();
        while (reader.Read())
        {
            result.Add(Map(reader));
        }
        reader.Close();
        return result;
    }
}

你的LincolnTelemetryMapper等具体实现不需要改,只要继承对应泛型基类就行。

第二步:实现通用工厂

工厂不依赖硬编码的类型判断,启动时自动扫描所有Mapper实现,通过容器解析实例:

public interface IMapperFactory
{
    MapperBase<T> GetMapper<T>();
    MapperBase GetMapper(Type entityType);
}

public class MapperFactory : IMapperFactory
{
    private readonly SimpleContainer _container;
    // 缓存实体类型和对应Mapper类型的映射
    private readonly Dictionary<Type, Type> _mapperTypeMap = new();

    public MapperFactory(SimpleContainer container)
    {
        _container = container;
        // 构造时自动扫描所有Mapper实现
        ScanAndRegisterMappers();
    }

    private void ScanAndRegisterMappers()
    {
        // 扫描当前程序集所有非抽象类
        var allClassTypes = Assembly.GetExecutingAssembly().GetTypes()
            .Where(t => t.IsClass && !t.IsAbstract);
        
        foreach (var mapperImplType in allClassTypes)
        {
            // 查找类型是否继承自MapperBase<>
            var baseType = mapperImplType.BaseType;
            while (baseType != null && !baseType.IsGenericType)
            {
                baseType = baseType.BaseType;
            }

            if (baseType == null || baseType.GetGenericTypeDefinition() != typeof(MapperBase<>))
            {
                continue;
            }

            // 提取泛型参数,也就是Mapper对应的实体类型
            Type entityType = baseType.GetGenericArguments()[0];
            _mapperTypeMap.Add(entityType, mapperImplType);
            // 把Mapper注册到容器,自动处理它的依赖注入
            _container.RegisterPerRequest(mapperImplType, null, mapperImplType);
        }
    }

    public MapperBase<T> GetMapper<T>()
    {
        return (MapperBase<T>)GetMapper(typeof(T));
    }

    public MapperBase GetMapper(Type entityType)
    {
        if (!_mapperTypeMap.TryGetValue(entityType, out Type mapperType))
        {
            throw new InvalidOperationException($"未找到实体类型{entityType.Name}对应的Mapper实现");
        }
        // 从容器获取Mapper实例,自动注入依赖
        return (MapperBase)_container.GetInstance(mapperType, null);
    }
}

第三步:Bootstrapper中注册工厂

不需要手动注册每个Mapper,只需要注册工厂即可:

var mapperFactory = new MapperFactory(container);
container.Instance<IMapperFactory>(mapperFactory);

使用方式

在业务类中通过构造函数注入IMapperFactory,传入实体类型就能获取对应的Mapper实例:

public class TelemetryDataService
{
    private readonly IMapperFactory _mapperFactory;

    // 构造函数自动注入工厂
    public TelemetryDataService(IMapperFactory mapperFactory)
    {
        _mapperFactory = mapperFactory;
    }

    public Collection<LincolnTelemetryModel> GetTelemetryData(IDbConnection conn)
    {
        using var reader = GetDataReader(conn); // 你自己的读数据逻辑
        var mapper = _mapperFactory.GetMapper<LincolnTelemetryModel>();
        return mapper.MapAllGeneric(reader);
    }
}

扩展说明

  • 后续新增Mapper时,只需要写对应的类继承MapperBase<对应实体>即可,不需要修改工厂代码,也不需要手动注册,启动时会自动扫描识别
  • 如果Mapper本身有其他依赖(比如日志组件、配置类),只要在Mapper的构造函数中声明,容器会自动注入,不需要手动传参
  • 你需要的DatabaseReader工厂可以用完全相同的逻辑实现:加非泛型Reader基类,工厂里增加Reader类型的扫描缓存即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:01:17