如何在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
相关产品推荐
相关产品推荐

