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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 00:10:20