如何自动注册实现IModel接口的Dapper模型类?
Dapper模型自动注册解决方案
问题背景
我使用ColumnAttributeTypeMapper实现Dapper模型与SQL列名的映射,目前需要为每个模型手动执行以下注册代码:
Dapper.SqlMapper.SetTypeMap(typeof(FooModel), new ColumnAttributeTypeMapper<FooModel>()); // 其他模型同理
为了实现自动化注册,我定义了IModel接口:
public interface IModel {}
并编写了扩展方法扫描所有实现该接口的类,尝试批量注册:
public static class WebApplicationExtensions { public static void MapModels(this WebApplication app) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var classes = assemblies.Distinct().SelectMany(x => x.GetTypes()) .Where(x => typeof(IModel).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract); foreach (var classe in classes) { Dapper.SqlMapper.SetTypeMap(classe, new ColumnAttributeTypeMapper<classe>()); } } } // 调用代码 app.MapModels();
但编译报错:'classe' is a variable but is used like a type,尝试其他写法也无效,请问如何在.NET中实现这些Dapper模型的自动注册?
解决方案
编译报错的核心原因是:泛型类型参数必须在编译时确定,而classe是运行时动态获取的Type变量,无法直接作为泛型参数使用。需要通过反射创建泛型实例来解决这个问题。
修正后的自动注册代码
public static class WebApplicationExtensions { public static void MapModels(this WebApplication app) { // 扫描所有包含IModel实现的程序集 var modelTypes = AppDomain.CurrentDomain.GetAssemblies() .Distinct() .SelectMany(assembly => assembly.GetTypes()) .Where(type => typeof(IModel).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract); // 获取ColumnAttributeTypeMapper的泛型定义(未绑定类型参数) var mapperGenericDefinition = typeof(ColumnAttributeTypeMapper<>); foreach (var modelType in modelTypes) { // 生成绑定当前模型类型的具体泛型类型 var mapperConcreteType = mapperGenericDefinition.MakeGenericType(modelType); // 创建泛型类型实例 var mapperInstance = Activator.CreateInstance(mapperConcreteType); // 注册Dapper类型映射 Dapper.SqlMapper.SetTypeMap(modelType, mapperInstance); } } }
关键逻辑说明
- 获取泛型定义:通过
typeof(ColumnAttributeTypeMapper<>)获取未绑定类型参数的泛型类型模板。 - 生成具体泛型类型:使用
MakeGenericType(modelType)将动态获取的模型类型绑定到泛型模板上,得到可实例化的具体类型。 - 创建实例并注册:通过
Activator.CreateInstance创建泛型实例,再传递给SqlMapper.SetTypeMap完成自动注册。
可选优化:缩小程序集扫描范围
如果项目程序集较多,可以只扫描包含IModel的程序集及其引用程序集,提升扫描效率:
public static void MapModels(this WebApplication app) { // 获取IModel所在的程序集 var baseAssembly = typeof(IModel).Assembly; // 扫描所有引用该程序集的程序集,加上基础程序集本身 var targetAssemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(assembly => assembly.GetReferencedAssemblies() .Any(asm => asm.FullName == baseAssembly.FullName)) .Append(baseAssembly) .Distinct(); var modelTypes = targetAssemblies .SelectMany(assembly => assembly.GetTypes()) .Where(type => typeof(IModel).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract); var mapperGenericDefinition = typeof(ColumnAttributeTypeMapper<>); foreach (var modelType in modelTypes) { var mapperConcreteType = mapperGenericDefinition.MakeGenericType(modelType); var mapperInstance = Activator.CreateInstance(mapperConcreteType); Dapper.SqlMapper.SetTypeMap(modelType, mapperInstance); } }
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

