EF Core 6 如何实现单个实体类映射生成多张数据表
EF Core 支持基于单个基础实体类映射多张数据表,无需为110个业务模型逐一创建单独的归档类即可实现需求,核心依赖EF Core内置的共享类型实体、阴影属性能力实现。
实现方案
核心原理
EF Core 的实体类型和CLR类不是强绑定的一对一关系:同一个CLR类可以注册为多个独立的实体类型,分别映射到不同的数据表,结构可以完全复用原实体的配置,不需要重复定义类。
具体操作步骤
- 动态注册归档表映射
重写DbContext的OnModelCreating方法,在完成原有业务实体的常规映射后,自动扫描所有业务实体,为每个实体注册对应归档表的映射,完全复用原实体的属性、主键、索引配置,代码示例:protected override void OnModelCreating(ModelBuilder modelBuilder) { // 此处保留你原有所有业务实体的配置逻辑 // ApplyConfigurationsFromAssembly 等原有配置代码无需改动 // 自动批量注册归档表 var businessEntityTypes = modelBuilder.Model.GetEntityTypes() .Where(e => !e.IsOwned() && !e.ClrType.IsAbstract) .ToList(); foreach (var entityType in businessEntityTypes) { // 以原实体CLR类型注册共享类型实体,名称为原实体名+Archive后缀 var archiveEntityBuilder = modelBuilder.SharedTypeEntity( type: entityType.ClrType, name: $"{entityType.ClrType.Name}Archive" ); // 拷贝原实体所有属性配置,保证表结构和原表一致 foreach (var prop in entityType.GetProperties()) { var propBuilder = archiveEntityBuilder.Property(prop.ClrType, prop.Name) .HasColumnType(prop.GetColumnType()) .IsRequired(prop.IsRequired); if (prop.GetDefaultValue() != null) propBuilder.HasDefaultValue(prop.GetDefaultValue()); } // 拷贝主键配置 var primaryKeyProps = entityType.FindPrimaryKey()?.Properties.Select(p => p.Name).ToArray(); if (primaryKeyProps != null) archiveEntityBuilder.HasKey(primaryKeyProps); // 映射到归档表,表名为原表名加_Archive后缀 archiveEntityBuilder.ToTable($"{entityType.GetTableName()}_Archive"); // 可选:统一给归档表加公共阴影字段,不需要修改原实体类 archiveEntityBuilder.Property<DateTime>("ArchivedTime").HasDefaultValueSql("SYSDATETIME()"); archiveEntityBuilder.Property<string>("ArchiveOperator", 64); } } - 封装通用仓储删除逻辑
在仓储层写统一的带归档删除方法,不需要为每个实体单独写逻辑:public async Task DeleteAndArchiveAsync<T>(T entity) where T : class { if (entity == null) throw new ArgumentNullException(nameof(entity)); // 将实体写入对应归档表 var archiveSet = _dbContext.Set<T>($"{typeof(T).Name}Archive"); archiveSet.Add(entity); // 从原表移除实体 _dbContext.Set<T>().Remove(entity); // 事务级保证两个操作原子性 using var transaction = await _dbContext.Database.BeginTransactionAsync(); try { await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } catch { await transaction.RollbackAsync(); throw; } }
注意事项
- 存在导航关联的实体,需要按外键依赖的先后顺序执行归档删除,先归档依赖表数据,再归档主表数据,避免外键约束报错
- 生成数据库迁移时,EF Core会自动识别所有新增的归档表,生成对应建表SQL,不需要手动编写表结构
- 归档表的外键配置可以根据业务需求选择是否保留,如果归档表不需要关联其他表,可以在注册归档实体时忽略外键配置,减少结构冗余
- 公共归档字段(归档时间、操作人)使用阴影属性实现,完全不侵入原有业务实体类,不会影响原有业务逻辑
内容的提问来源于stack exchange,提问作者Nolan Blew
相关产品推荐
相关产品推荐

