EF Core单SQL表映射一对多C#实体问题求助
解决EF Core单表映射为一对多实体的方案
前提假设
假设你的SQL表结构类似如下(可根据实际字段调整):
CREATE TABLE ModelItemData ( Model NVARCHAR(50) NOT NULL, Version NVARCHAR(20) NOT NULL, ItemId INT IDENTITY(1,1) NOT NULL, ItemName NVARCHAR(100) NOT NULL, ItemValue NVARCHAR(MAX) NULL, PRIMARY KEY (Model, Version, ItemId) )
1. 定义实体类
将单表拆分为一对多的两个实体,父实体对应Model+Version的分组,子实体对应单条明细:
// 父实体:对应Model+Version的分组 public class ModelVersionControl { // 复合主键:唯一标识一组版本 public string Model { get; set; } public string Version { get; set; } // 导航属性:关联该版本下的所有明细Item public ICollection<Item> Items { get; set; } = new List<Item>(); } // 子实体:对应表中的每条明细记录 public class Item { // 外键:关联父实体的复合主键 public string Model { get; set; } public string Version { get; set; } // 自身唯一标识(根据表结构调整,可设为自增ID) public int ItemId { get; set; } // 业务字段 public string ItemName { get; set; } public string ItemValue { get; set; } // 反向导航属性:关联父实体 public ModelVersionControl ModelVersion { get; set; } }
2. 配置DbContext映射
通过Fluent API显式配置实体与表的映射、主键及一对多关系:
public class MyDbContext : DbContext { public DbSet<ModelVersionControl> ModelVersions { get; set; } public DbSet<Item> Items { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // 替换为你的数据库连接字符串 optionsBuilder.UseSqlServer("YourConnectionString"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置父实体映射 modelBuilder.Entity<ModelVersionControl>(entity => { // 设置复合主键 entity.HasKey(m => new { m.Model, m.Version }); // 指定映射到目标SQL表 entity.ToTable("ModelItemData"); // 配置一对多关系:一个分组对应多个明细 entity.HasMany(m => m.Items) .WithOne(i => i.ModelVersion) .HasForeignKey(i => new { i.Model, i.Version }) .OnDelete(DeleteBehavior.Cascade); }); // 配置子实体映射 modelBuilder.Entity<Item>(entity => { // 设置子实体主键(与SQL表主键一致) entity.HasKey(i => new { i.Model, i.Version, i.ItemId }); // 同样映射到目标SQL表 entity.ToTable("ModelItemData"); }); } }
3. 无Group By的查询方式
直接通过导航属性加载关联数据,无需手动分组:
using (var context = new MyDbContext()) { // 加载所有版本分组及对应的明细 var allVersionGroups = context.ModelVersions .Include(m => m.Items) .ToList(); // 查询指定Model和Version的分组数据 var specificGroup = context.ModelVersions .Include(m => m.Items) .FirstOrDefault(m => m.Model == "OrderModel" && m.Version == "v2.1"); }
常见问题排查
- 确保父实体的复合主键与子实体的外键字段类型、名称完全匹配
- 必须显式配置两个实体映射到同一个SQL表,否则EF会默认生成两张表
- 若SQL表主键不是复合键,可将子实体主键设为单独的
ItemId,但外键仍需关联Model+Version
内容的提问来源于stack exchange,提问作者Emad Ali
相关产品推荐
相关产品推荐

