当前位置: 首页 > news >正文

EF Core 与 MySQL:迁移和关系配置详解

 

1. EF Core 中的关系类型

Entity Framework Core 支持三种主要的关系类型:

一对一关系 (One-to-One)

一个实体实例只与另一个实体实例相关联。例如:一个用户有一个用户资料。

csharp
public class User
{public int Id { get; set; }public string Username { get; set; }public UserProfile Profile { get; set; } // 导航属性
}public class UserProfile
{public int Id { get; set; }public string FullName { get; set; }public DateTime DateOfBirth { get; set; }public int UserId { get; set; } // 外键public User User { get; set; } // 导航属性
}

一对多关系 (One-to-Many)

一个实体实例与多个另一个实体实例相关联。例如:一个博客有多篇文章。

csharp
public class Blog
{public int Id { get; set; }public string Title { get; set; }public List<Post> Posts { get; set; } // 导航属性
}public class Post
{public int Id { get; set; }public string Title { get; set; }public string Content { get; set; }public int BlogId { get; set; } // 外键public Blog Blog { get; set; } // 导航属性
}

多对多关系 (Many-to-Many)

多个实体实例与多个另一个实体实例相关联。例如:一个学生可以选多门课程,一门课程可以有多个学生。

csharp
public class Student
{public int Id { get; set; }public string Name { get; set; }public List<Course> Courses { get; set; } // 导航属性
}public class Course
{public int Id { get; set; }public string Title { get; set; }public List<Student> Students { get; set; } // 导航属性
}// 连接实体(EF Core 5.0+ 可以隐式处理,但显式定义更灵活)
public class StudentCourse
{public int StudentId { get; set; }public Student Student { get; set; }public int CourseId { get; set; }public Course Course { get; set; }public DateTime EnrollmentDate { get; set; }
}

2. 使用 Fluent API 配置关系

Fluent API 提供了更精细的控制方式来配置关系:

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{// 一对一关系配置modelBuilder.Entity<User>().HasOne(u => u.Profile).WithOne(up => up.User).HasForeignKey<UserProfile>(up => up.UserId);// 一对多关系配置modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).HasForeignKey(p => p.BlogId).OnDelete(DeleteBehavior.Cascade); // 级联删除// 多对多关系配置 (EF Core 5.0+)modelBuilder.Entity<Student>().HasMany(s => s.Courses).WithMany(c => c.Students).UsingEntity<StudentCourse>(j => j.HasOne(sc => sc.Course).WithMany().HasForeignKey(sc => sc.CourseId),j => j.HasOne(sc => sc.Student).WithMany().HasForeignKey(sc => sc.StudentId),j => {j.HasKey(sc => new { sc.StudentId, sc.CourseId });j.Property(sc => sc.EnrollmentDate).HasDefaultValueSql("CURRENT_TIMESTAMP");});// 配置索引modelBuilder.Entity<Post>().HasIndex(p => p.Title).IsUnique();// 配置表名和列名modelBuilder.Entity<User>().ToTable("Users").Property(u => u.Username).HasColumnName("user_name").HasMaxLength(50);
}

3. 处理迁移中的常见问题

创建和应用迁移

bash
# 创建迁移
dotnet ef migrations add AddRelationships# 应用迁移
dotnet ef database update# 回滚迁移
dotnet ef database update PreviousMigrationName# 删除最后一次迁移
dotnet ef migrations remove

解决迁移冲突

当多个开发人员同时创建迁移时,可能会产生冲突。解决方法:

  1. 协调团队成员,确保一次只有一个人创建迁移

  2. 使用 dotnet ef migrations script 生成SQL脚本,手动合并更改

  3. 删除冲突的迁移,重新创建

处理模型更改后的迁移

当模型更改后,EF Core 可能无法自动检测所有更改。解决方法:

  1. 仔细检查生成的迁移代码

  2. 手动修改迁移文件以包含所有必要的更改

  3. 使用 dotnet ef migrations add 创建新的迁移

处理外键约束问题

csharp
// 在迁移中处理外键约束
migrationBuilder.AddForeignKey(name: "FK_Posts_Blogs_BlogId",table: "Posts",column: "BlogId",principalTable: "Blogs",principalColumn: "Id",onDelete: ReferentialAction.Cascade);

处理MySQL特定的迁移问题

csharp
// 在迁移中处理MySQL特定配置
migrationBuilder.AlterColumn<string>(name: "Title",table: "Posts",type: "varchar(255)",maxLength: 255,nullable: false,collation: "utf8mb4_unicode_ci",oldClrType: typeof(string),oldType: "longtext",oldNullable: false).Annotation("MySql:CharSet", "utf8mb4");

4. 数据种子 (Seed Data)

在迁移中配置种子数据

csharp
// 在迁移的Up方法中添加种子数据
protected override void Up(MigrationBuilder migrationBuilder)
{migrationBuilder.InsertData(table: "Blogs",columns: new[] { "Id", "Title" },values: new object[] { 1, "Default Blog" });
}// 在迁移的Down方法中移除种子数据
protected override void Down(MigrationBuilder migrationBuilder)
{migrationBuilder.DeleteData(table: "Blogs",keyColumn: "Id",keyValue: 1);
}

在DbContext中配置种子数据

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{modelBuilder.Entity<Blog>().HasData(new Blog { Id = 1, Title = "Default Blog" },new Blog { Id = 2, Title = "Secondary Blog" });modelBuilder.Entity<Post>().HasData(new Post { Id = 1, Title = "First Post", Content = "Hello World", BlogId = 1 },new Post { Id = 2, Title = "Second Post", Content = "EF Core is awesome", BlogId = 1 });
}

使用自定义初始化逻辑

csharp
public static class DbInitializer
{public static void Initialize(ApplicationDbContext context){// 确保数据库已创建context.Database.EnsureCreated();// 检查是否已有数据if (context.Blogs.Any()){return; // 数据库已经 seeded}// 添加初始数据var blogs = new Blog[]{new Blog{Title="Technology Blog"},new Blog{Title="Food Blog"}};foreach (var blog in blogs){context.Blogs.Add(blog);}context.SaveChanges();var posts = new Post[]{new Post{Title="Introduction to EF Core", Content="...", BlogId=1},new Post{Title="Best Pizza Recipe", Content="...", BlogId=2}};foreach (var post in posts){context.Posts.Add(post);}context.SaveChanges();}
}

在应用程序启动时调用初始化

csharp
// 在Program.cs或Startup.cs中
public static void Main(string[] args)
{var host = CreateHostBuilder(args).Build();using (var scope = host.Services.CreateScope()){var services = scope.ServiceProvider;try{var context = services.GetRequiredService<ApplicationDbContext>();DbInitializer.Initialize(context);}catch (Exception ex){var logger = services.GetRequiredService<ILogger<Program>>();logger.LogError(ex, "An error occurred while seeding the database.");}}host.Run();
}

5. 处理复杂场景

条件种子数据

csharp
public static void Initialize(ApplicationDbContext context)
{// 只在开发环境中添加测试数据if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"){if (!context.Blogs.Any()){context.Blogs.AddRange(new Blog { Title = "Test Blog 1" },new Blog { Title = "Test Blog 2" });context.SaveChanges();}}// 在所有环境中添加必要的基础数据if (!context.Roles.Any()){context.Roles.AddRange(new Role { Name = "Administrator" },new Role { Name = "User" });context.SaveChanges();}
}

使用JSON文件存储种子数据

csharp
public static void Initialize(ApplicationDbContext context)
{if (!context.Blogs.Any()){var seedDataPath = Path.Combine(Directory.GetCurrentDirectory(), "SeedData");var blogsJson = File.ReadAllText(Path.Combine(seedDataPath, "blogs.json"));var blogs = JsonSerializer.Deserialize<List<Blog>>(blogsJson);context.Blogs.AddRange(blogs);context.SaveChanges();}
}

总结

本教程详细介绍了EF Core与MySQL中的关系配置、迁移处理和种子数据管理。关键点包括:

  1. 使用Fluent API精细配置一对一、一对多和多对多关系

  2. 正确处理迁移创建、应用和回滚

  3. 解决迁移过程中的常见问题

  4. 使用多种方法添加和管理种子数据

  5. 处理复杂场景如条件种子数据和外部数据源

正确配置关系和迁移是构建健壮数据访问层的关键,而合理的种子数据策略可以大大简化开发和测试过程。

 
 
 
http://www.wxhsa.cn/company.asp?id=5159

相关文章:

  • 《原子习惯》-读书笔记2
  • CF1626D 题解
  • Python 集合运算:并集、交集、差集全解析
  • 第一周数据可视化作业
  • 用 C++ + OpenCV + Tesseract 实现英文数字验证码识别
  • java 第一节课课前提问
  • 二进制解码器、选通器和分配器
  • 2025最新版 Photoshop软件免费下载安装完整教程(PS2025)超详细安装教程
  • nac一键卸载软件脚本
  • 交叉编译openharmony版本的openssh
  • 为什么不建议在 Docker 中跑 MySQL
  • CFD
  • [MCP][05]Elicitation示例
  • Warsaw主题关闭导航条
  • Python Socket网络编程(2)
  • PS2025安装包下载及PS2025安装包安装教程详细步骤(包含安装包下载链接)
  • Nature Genetics | 本周最新文献速递
  • 关于go里切片作为函数参数时是引用传递还是值传递
  • DRAN读写循环
  • 数据结构操作相关
  • Neisbitt 不等式的证法
  • 端口转发神器Rinetd:轻量级安装与配置指南
  • C语言中递归思想的应用
  • WITH RECURSIVE 递归公用表表达式(CTE)
  • leetcode 3541. 找到频率最高的元音和辅音 便捷
  • 匿名递归与不动点组合子
  • Markdown学习Day01
  • flutter compass结构代码分析
  • 25.9.15
  • 二十八、共享内存多处理器的基本概念