如何通过特性自动注册.NET7 WebAPI中Dapper的实体映射?
自动注册Dapper Column特性映射的解决方案
问题背景
在.NET 7 WebAPI中使用Dapper时,通过自定义Column特性实现数据库列与实体类属性的映射,但目前需在Program.cs中手动为每个实体调用SqlMapper.SetTypeMap,频繁修改易引发版本控制合并冲突,需要自动化的映射注册方案。
方案1:自定义标记特性+程序集扫描
通过自定义特性标记需要注册映射的实体,再扫描程序集自动完成注册:
- 创建标记特性
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class DapperMappedAttribute : Attribute { }
- 为实体类添加标记
[DapperMapped] public class Customer { public int CustomerID { get; set; } [Column(Name = "Customer_Name")] public string Name { get; set; } }
- 在Program.cs中添加自动注册逻辑
// 扫描当前程序集内所有标记了DapperMappedAttribute的类型 var mappedTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.GetCustomAttributes(typeof(DapperMappedAttribute), inherit: false).Any()); foreach (var type in mappedTypes) { // 动态创建对应类型的ColumnAttributeTypeMapper实例 var mapperGenericType = typeof(ColumnAttributeTypeMapper<>).MakeGenericType(type); var mapperInstance = Activator.CreateInstance(mapperGenericType); // 调用Dapper的SetTypeMap完成注册 typeof(SqlMapper) .GetMethod("SetTypeMap", new[] { typeof(Type), typeof(SqlMapper.ITypeMap) }) .Invoke(null, new object[] { type, mapperInstance }); }
方案2:自动扫描含Column特性的实体
若不想额外添加标记特性,可直接扫描所有包含Column特性属性的实体类:
var targetAssembly = Assembly.GetExecutingAssembly(); // 筛选所有包含Column特性属性的非抽象类 var entityTypes = targetAssembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.GetProperties().Any(p => p.GetCustomAttributes(typeof(ColumnAttribute), inherit: false).Any())); foreach (var type in entityTypes) { var mapperGenericType = typeof(ColumnAttributeTypeMapper<>).MakeGenericType(type); var mapperInstance = Activator.CreateInstance(mapperGenericType); typeof(SqlMapper) .GetMethod("SetTypeMap", new[] { typeof(Type), typeof(SqlMapper.ITypeMap) }) .Invoke(null, new object[] { type, mapperInstance }); }
可选优化:缓存映射逻辑提升性能
原ColumnAttributeTypeMapper可添加静态缓存,避免每次反射查询属性:
public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper { private static readonly Dictionary<string, PropertyInfo> _columnToPropertyMap; static ColumnAttributeTypeMapper() { // 预加载并缓存列名与属性的映射关系 _columnToPropertyMap = typeof(T).GetProperties() .SelectMany(prop => prop.GetCustomAttributes<ColumnAttribute>(false) .Select(attr => new { ColumnName = attr.Name, Property = prop })) .ToDictionary(kvp => kvp.ColumnName, kvp => kvp.Property, StringComparer.OrdinalIgnoreCase); } public ColumnAttributeTypeMapper() : base(new SqlMapper.ITypeMap[] { new CustomPropertyTypeMap( typeof(T), (type, columnName) => _columnToPropertyMap.TryGetValue(columnName, out var prop) ? prop : type.GetProperties().FirstOrDefault(p => p.Name.Equals(columnName, StringComparison.OrdinalIgnoreCase)) ), new DefaultTypeMap(typeof(T)) }) { } }
注意事项
- 若实体类分布在多个程序集,可通过
AppDomain.CurrentDomain.GetAssemblies()遍历所有加载的程序集,注意过滤系统程序集避免无效扫描。 - 注册逻辑需放在
Program.cs中应用启动前执行,确保Dapper执行数据库操作前已完成映射配置。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

