EntityFramework 外键值映射
如果在 EF OnModelCreating 中配置了实体外键映射,也就是 SQL Server 中的 ForeignKey,那么我们在添加实体的时候,主实体的主键值会自动映射到子实体的外键值,并且这个操作在一个 SaveChanges 中,但如果没有在 OnModelCreating 中进行外键映射配置,我们添加实体的时候,就不会自动映射外键值了,什么意思呢?我们先看一个示例代码:
public class SchoolDbContext : DbContext
{
public SchoolDbContext()
: base("db_school")
{ }
public DbSet<Student> Students { get; set; }
public DbSet<Class> Classs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasKey(n => n.StudentId);
modelBuilder.Entity<Class>()
.HasKey(n => n.ClassId);
base.OnModelCreating(modelBuilder);
}
}
public class Student
{
public int StudentId { get; set; }
public int ClassId { get; set; }
public string Name { get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string Name { get; set; }
}
示例很简单,Class 和 Student 是一对多关系,但我们并没有在 OnModelCreating 中进行外键映射配置,所以生成到 SQL Server 的 db_school 数据库,会是这样:
可以看到 Student 中的 ClassId 字段并不是外键,下面我们添加 Student 和 Class 实体:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss1" };
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student1" };
context.Classs.Add(entityClass);
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
执行结果:
可以看到,Student 表中的 ClassId 值是 0,而并不是我们预想的 1,这是一个问题,在不增加外键的情况下,我们一般会这样解决:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss2" };
context.Classs.Add(entityClass);
context.SaveChanges();
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student2" };
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
执行结果:
这种处理方式,虽然“解决”上面的问题,但其实有很多的隐患,多执行一次 SaveChanges,EF 就会多发起一次请求,增加了性能开销,并且 SaveChanges 是事务性的,如果第一个执行成功了,第二个执行失败了,这时候第一个事务并不会回滚,因为它独立于第二个,所以,最后就会造成数据的不一致性,虽然几率非常点,但我们应该尽量避免。
那有没有更好的解决方式呢?这个问题我之前有点想复杂了,其实解决非常简单,就是在 Student 实体中添加 virtual 修饰的 Class 属性,就可以了,如下:
public class Student
{
public int StudentId { get; set; }
public int ClassId { get; set; }
public string Name { get; set; }
public virtual Class Class { get; set; }//添加属性
}
需要注意的是,我们并不需要在 OnModelCreating 中进行 Class 和 ClassId 的映射配置,EF 会自动查找 ClassId(属性名 + Id),所以,“外键”命名要注意规范统一,如果命名为 Class_Id 就无效了。
再次执行添加实体的代码,发现会报错:
什么意思呢?就是实体更改了,需要进行 EF 迁移,如果你进行 EF 迁移的话,会发现,虽然我们没有在 OnModelCreating 中进行 ClassId 外键映射配置,但 EF 也会自动映射 ForeignKey 到数据库的,所以代码命名尽量规范些,EF 是比较“智能”的。
我们解决这个问题的前提条件是“不增加外键配置”,所以我们要让 EF 忽略实体更改:
public SchoolDbContext()
: base("db_school")
{
Database.SetInitializer<SchoolDbContext>(null);//忽略映射
}
再次执行添加实体代码:
static void Main(string[] args)
{
using (var context = new SchoolDbContext())
{
var entityClass = new Class() { Name = "calss3" };
var entityStudent = new Student() { ClassId = entityClass.ClassId, Name = "student3" };
context.Classs.Add(entityClass);
context.Students.Add(entityStudent);
context.SaveChanges();
}
}
执行结果:
微信公众号:你好架构
出处:http://www.cnblogs.com/xishuai/
公众号会不定时的分享有关架构的方方面面,包含并不局限于:Microservices(微服务)、Service Mesh(服务网格)、DDD/TDD、Spring Cloud、Dubbo、Service Fabric、Linkerd、Envoy、Istio、Conduit、Kubernetes、Docker、MacOS/Linux、Java、.NET Core/ASP.NET Core、Redis、RabbitMQ、MongoDB、GitLab、CI/CD(持续集成/持续部署)、DevOps等等。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。