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

如何在RepoDB中使用运行时动态(重新)编译程序集的类型?

动态编译类在RepoDB中类型转换错误的解决方法

问题背景

项目监听C#文件目录,文件变化时重新编译并加载程序集。但将这些动态编译的类作为RepoDB的Query/Insert等方法的类型参数时,出现类型转换错误:

[A]MyNamespace.MyModelClass cannot be cast to [B]MyNamespace.MyModelClass. Type A originates from 'DynamicallyRecompiledAssembly001, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at location 'C:\Path\DynamicallyRecompiledAssembly001.dll'. Type B originates from 'DynamicallyRecompiledAssembly002, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\Path\DynamicallyRecompiledAssembly002.dll'.

已尝试的无效方案:

  • 给类添加file访问修饰符,期望类名变化,但实际类名未改变,错误依旧
  • 在类静态构造函数中清空RepoDB的部分映射和缓存(如ClassMapper.Clear()、TypeMapCache.Flush()等),但仍出现相同错误

解决方案

1. 彻底清理所有RepoDB相关静态缓存

之前的清理操作漏掉了部分关键缓存项,需要在重新加载新程序集之前执行全面清理:

// 清理所有映射器
RepoDb.ClassMapper.Clear();
RepoDb.TypeMapper.Clear();
RepoDb.PrimaryMapper.Clear();
RepoDb.IdentityMapper.Clear();
RepoDb.PropertyMapper.Clear();
RepoDb.ClassHandlerMapper.Clear();
RepoDb.PropertyHandlerMapper.Clear();
RepoDb.PropertyValueAttributeMapper.Clear();

// 清理所有缓存
RepoDb.TypeMapCache.Flush();
RepoDb.ClassMappedNameCache.Flush();
RepoDb.PropertyMappedNameCache.Flush();
RepoDb.PropertyValueCache.Flush();
RepoDb.PropertyTypeCache.Flush();
RepoDb.PrimaryCache.Flush();
RepoDb.IdentityCache.Flush();
RepoDb.ExpandoObjectCache.Flush();
RepoDb.DataReaderToEntityCache.Flush();
RepoDb.EntityToDbParametersCache.Flush();

注意:不要清理DbSettingMapper和StatementBuilderMapper,这些是RepoDB核心功能依赖的全局配置,清空会导致框架无法正常工作。

2. 统一程序集加载上下文

动态编译的程序集如果使用不同加载上下文(比如LoadFrom和Default),CLR会将同名称类型视为不同类型。确保每次重新编译时,使用相同上下文加载程序集,推荐直接加载程序集字节流:

// 编译C#文件得到字节流
byte[] assemblyBytes = CompileMyFiles();
// 直接加载字节流,使用默认上下文
Assembly newAssembly = Assembly.Load(assemblyBytes);
// 获取新类型
Type newModelType = newAssembly.GetType("MyNamespace.MyModelClass");

3. 避免跨程序集类型混用

重新加载程序集后,确保所有业务逻辑都切换到新程序集的类型,不要混合使用新旧类型的实例,避免CLR因类型来源不同抛出转换错误。

4. 自定义RepoDB缓存键策略(进阶)

如果上述方法无效,可以自定义缓存逻辑,让RepoDB将不同程序集的同名称类型视为不同缓存项:

public class AssemblyAwareCache : ICache
{
    private readonly ICache _innerCache = new MemoryCache();

    public object Get(string key, Type type)
    {
        var assemblyKey = $"{key}_{type.Assembly.FullName}";
        return _innerCache.Get(assemblyKey, type);
    }

    public void Set(string key, Type type, object value)
    {
        var assemblyKey = $"{key}_{type.Assembly.FullName}";
        _innerCache.Set(assemblyKey, type, value);
    }

    // 实现其他ICache接口方法,均按此规则处理缓存键
}

// 应用启动时替换默认缓存
RepoDb.GlobalConfiguration.SetCache(new AssemblyAwareCache());

为什么之前的清理无效?

之前在类静态构造函数中清理缓存的时机错误:静态构造函数是新类型第一次被访问时执行,此时RepoDB可能已缓存旧类型映射,且旧类型实例可能仍在业务逻辑中流转。正确时机是卸载旧程序集、加载新程序集之前执行缓存清理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 07:54:57