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 数据库,会是这样:
![](https://images2015.cnblogs.com/blog/435188/201510/435188-20151009160207846-1014486844.png)
可以看到 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();
}
}
执行结果:
![](https://images2015.cnblogs.com/blog/435188/201510/435188-20151009160840424-1005178222.png)
可以看到,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();
}
}
执行结果:
![](https://images2015.cnblogs.com/blog/435188/201510/435188-20151009161247596-1472803800.png)
这种处理方式,虽然“解决”上面的问题,但其实有很多的隐患,多执行一次 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 就无效了。
再次执行添加实体的代码,发现会报错:
![](https://images2015.cnblogs.com/blog/435188/201510/435188-20151009162453659-1243691437.png)
什么意思呢?就是实体更改了,需要进行 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();
}
}
执行结果:
![](https://images2015.cnblogs.com/blog/435188/201510/435188-20151009163141549-1317288580.png)
相关文章
- 使用Mybatis的TypeHandler加解密数据
- logstash收集的日志输出到elasticsearch中
- 使用logstash的grok插件解析springboot日志
- logstash的mutate过滤器的使用
- logstash multi pipeline的使用
- 使用logstash的input file filter收集日志文件
- logstash的安装和简单使用
- elasticsearch父子文档处理(join)
- elasticsearch基于RBAC认证和集群之间的TLS通讯
- 使用cerebro可视化ElasticSearch集群信息
- elasticsearch地理位置查询
- elasticsearch使用ik中文分词器
- es7集群的搭建
- 基于ImportBeanDefinitionRegistrar和FactoryBean动态注入Bean到Spring容器中
- 基于自定义Validator来验证枚举类型
- Mac上安装Grafana
- Prometheus基于Eureka的服务发现
- SpringBoot整合Prometheus
- sql_exporter的使用
- mysqld_exporter监控mysql信息