如何通过EF Core将Foo类的List<string> Types属性转为SQL Server表
完整解决方案:将Foo的Types列表存入独立SQL Server表
1. 修正实体类定义
首先需要补充FooType实体类,并调整Foo类的属性结构,确保EF Core能识别实体关系:
// FooType实体:对应数据库中的FooTypes表 public class FooType { public int FooId { get; set; } public string Type { get; set; } // 导航属性:关联到对应的Foo public Foo Foo { get; set; } } // Foo实体:调整后适配EF Core关系映射 public class Foo { public int Id { get; set; } public string Name { get; set; } // 其他原有属性... // 【可选】如果需要直接接收API的Types数据,添加非映射属性 [NotMapped] public List<string> Types { get; set; } // EF Core导航属性:关联当前Foo对应的所有FooType public List<FooType> FooTypes { get; set; } = new List<FooType>(); }
2. 完善DbContext配置
你的原配置缺少关键的实体属性关联和约束,以下是修正后的完整配置:
public class FooDbContext : DbContext { public DbSet<Foo> Foos { get; set; } public DbSet<FooType> FooTypes { get; set; } // 构造函数:传入数据库连接配置 public FooDbContext(DbContextOptions<FooDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置Foo实体 modelBuilder.Entity<Foo>() .HasKey(f => f.Id); // 主键为Id modelBuilder.Entity<Foo>() .ToTable("Foos") .Property(f => f.Name) .HasMaxLength(50) .IsRequired(); // 标记Name为必填字段 // 配置FooType实体 modelBuilder.Entity<FooType>() .HasKey(ft => new { ft.FooId, ft.Type }); // 复合主键:避免同一Foo下重复存储相同Type modelBuilder.Entity<FooType>() .ToTable("FooTypes") .Property(ft => ft.Type) .HasMaxLength(100) .IsRequired(); // 标记Type为必填字段 // 配置一对多关系:一个Foo对应多个FooType modelBuilder.Entity<Foo>() .HasMany(f => f.FooTypes) .WithOne(ft => ft.Foo) .HasForeignKey(ft => ft.FooId) .OnDelete(DeleteBehavior.Cascade); // 删除Foo时自动删除关联的FooType(可根据需求调整为Restrict) } }
3. 处理API数据的转换与存储
从API获取JSON并反序列化后,需要将Types列表转换为FooType实体集合,再通过EF Core保存到数据库:
方式一:直接使用带[NotMapped]属性的Foo类
// 从API获取JSON并反序列化为Foo列表 string apiJson = await new HttpClient().GetStringAsync("your-api-url"); var fooList = JsonSerializer.Deserialize<List<Foo>>(apiJson); // 将每个Foo的Types转换为FooType对象 foreach (var foo in fooList) { if (foo.Types != null) { foreach (var type in foo.Types) { foo.FooTypes.Add(new FooType { Type = type }); } foo.Types = null; // 清空非映射属性 } } // 保存到数据库 using (var context = new FooDbContext(options)) { context.Foos.AddRange(fooList); await context.SaveChangesAsync(); }
方式二:使用DTO分离API数据与实体类(更推荐)
如果不想修改原有Foo类的结构,可以创建DTO类接收API数据,再映射到实体:
// API数据接收DTO public class FooDto { public int Id { get; set; } public string Name { get; set; } // 其他API返回属性... public List<string> Types { get; set; } } // 反序列化并映射 string apiJson = await new HttpClient().GetStringAsync("your-api-url"); var dtoList = JsonSerializer.Deserialize<List<FooDto>>(apiJson); var fooList = dtoList.Select(dto => new Foo { Id = dto.Id, Name = dto.Name, // 其他属性映射... FooTypes = dto.Types?.Select(t => new FooType { Type = t }).ToList() ?? new List<FooType>() }).ToList(); // 保存到数据库 using (var context = new FooDbContext(options)) { context.Foos.AddRange(fooList); await context.SaveChangesAsync(); }
关键说明
- 原配置的核心问题是缺少
FooType实体定义,且Foo类没有对应的导航属性FooTypes,导致EF无法识别关系 - 复合主键
{FooId, Type}确保同一Foo下不会重复存储相同的Type值 - 可以根据业务需求调整
OnDelete行为(比如DeleteBehavior.Restrict禁止删除关联了FooType的Foo)
内容的提问来源于stack exchange,提问作者Bruno S.
相关产品推荐
相关产品推荐

