.NET Core项目中如何让Dapper复用Entity Framework Fluent API映射?
复用EF Core Fluent API映射规则给Dapper
需求背景
在.NET Core 6.0项目中同时使用Entity Framework Core(EF Core)和Dapper,希望复用EF Core的Fluent API映射规则,实现实体映射的集中管理,避免重复配置;同时保留EF Core的迁移功能,且不修改Dapper的查询语句(不使用列别名)。
以PersonalData实体为例:
- 数据库视图
VW_PERSONAL_DATA返回列:ID, NAME, NAME_AND_SURNAME - 实体类定义:
public class PersonalData { public string? id { get; set; } public string? MainName { get; set; } public string? FullName { get; set; } }
- EF Core Fluent API配置:
public class PersonalDataConfig : IEntityTypeConfiguration<PersonalData> { public void Configure(EntityTypeBuilder<PersonalData> builder) { builder.ToView("VW_PERSONAL_DATA").HasNoKey(); builder.Property(o => o.Id).HasColumnName("ID"); builder.Property(o => o.MainName).HasColumnName("NAME"); builder.Property(o => o.FullName).HasColumnName("NAME_AND_LASTNAME"); } }
- DbContext已应用该配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfiguration(new PersonalDataConfig()); }
EF Core查询能正常映射,但Dapper直接查询时映射失败:
var pd = await _repository.QueryFirstOrDefaultAsync<PersonalData>($"SELECT * FROM VW_PERSONAL_DATA", cancellationToken: cancellationToken);
解决方案
通过读取EF Core已构建的模型元数据,为Dapper注册自定义类型映射,让Dapper使用EF的列-属性映射规则。
1. 提取EF Core映射关系
创建工具类从DbContext中获取实体的属性与列名映射:
public static class EfCoreMappingHelper { public static Dictionary<string, string> GetPropertyColumnMap<TEntity>(DbContext dbContext) where TEntity : class { var entityType = dbContext.Model.FindEntityType(typeof(TEntity)); if (entityType == null) throw new InvalidOperationException($"实体 {typeof(TEntity).Name} 未在DbContext中配置"); return entityType.GetProperties() .ToDictionary( prop => prop.Name, prop => prop.GetColumnName(), StringComparer.OrdinalIgnoreCase ); } }
2. 实现Dapper自定义类型映射
继承Dapper的ITypeMap接口,使用EF的映射关系匹配列和属性:
public class EfCoreDapperTypeMap<TEntity> : SqlMapper.ITypeMap where TEntity : class { private readonly Dictionary<string, string> _columnToPropertyMap; private readonly SqlMapper.ITypeMap _defaultTypeMap; public EfCoreDapperTypeMap(DbContext dbContext) { var propertyColumnMap = EfCoreMappingHelper.GetPropertyColumnMap<TEntity>(dbContext); _columnToPropertyMap = propertyColumnMap.ToDictionary( kv => kv.Value, kv => kv.Key, StringComparer.OrdinalIgnoreCase ); _defaultTypeMap = SqlMapper.GetTypeMap(typeof(TEntity)); } public ConstructorInfo FindConstructor(string[] names, Type[] types) => _defaultTypeMap.FindConstructor(names, types); public ConstructorInfo FindExplicitConstructor() => _defaultTypeMap.FindExplicitConstructor(); public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName) { if (_columnToPropertyMap.TryGetValue(columnName, out var propertyName)) return _defaultTypeMap.GetConstructorParameter(constructor, propertyName); return _defaultTypeMap.GetConstructorParameter(constructor, columnName); } public SqlMapper.IMemberMap GetMember(string columnName) { if (_columnToPropertyMap.TryGetValue(columnName, out var propertyName)) return _defaultTypeMap.GetMember(propertyName); return _defaultTypeMap.GetMember(columnName); } }
3. 注册映射到Dapper
在项目启动时(如Program.cs),从DI容器获取DbContext,为需要复用映射的实体注册自定义类型映射:
var builder = WebApplication.CreateBuilder(args); // 注册DbContext builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); var app = builder.Build(); // 初始化Dapper映射 using (var scope = app.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>(); // 为PersonalData注册映射 SqlMapper.SetTypeMap(typeof(PersonalData), new EfCoreDapperTypeMap<PersonalData>(dbContext)); // 其他实体同理注册 } // 中间件配置... app.Run();
4. 验证查询
现在直接执行Dapper查询即可自动应用EF的映射规则,无需修改查询语句:
var pd = await _repository.QueryFirstOrDefaultAsync<PersonalData>($"SELECT * FROM VW_PERSONAL_DATA", cancellationToken: cancellationToken);
注意事项
- 该方案仅处理简单的属性-列映射,若实体包含继承、导航属性或阴影属性,需根据实际场景调整映射提取逻辑。
- 确保注册映射时DbContext已完成模型构建(即
OnModelCreating执行完毕),因此必须从DI容器获取初始化后的DbContext实例。 - 完全保留EF Core的迁移功能,因为EF仍使用原有的Fluent API配置。
内容的提问来源于stack exchange,提问作者Mesquita Engineer
相关产品推荐
相关产品推荐

