Entity Framework 6.0 code first 一对一关系(one to one) 详解
欢迎转载,转载请注明出处 http://www.cnblogs.com/youyoubaishu/p/3866697.html
最近研究了一下 Entity Framework一对一关系的mapping,略有收获,在此进行一些总结,希望对各位读者也有所帮助:
有两点非常重要,需要大家注意:
1. Entity Framework code first convention里,建议如果使用一对一关系,需要共享主键,即一张表使用另一张表的主键做为自己的主键。
2. 如果不共享主键,是不是有办法做呢?答案是有的,之后我会介绍,但是个人不建议使用,可能有未知错误,而且个人以为没有必要。
下面使用一个例子来说明一对一关系:
用例逻辑: 一个person类,一个user类,user类里有登录信息。每个user一定是一个person,反之一个person不一定有一个登录帐号。
共享主键的方法
直接上代码, 先是model:
public class Person { public int PersonId { get; set; } public string LastName { get; set; } public string GivenName { get; set; } public string MiddleName { get; set; } public string Gender { get; set; } public int UserId { get; set; }//这里是Foreign Key public virtual User UserAccount { get; set; }//这里是Navigation Property }
public class User { public int PersonId { get; set; } public string UserName { get; set; }
public string Password { get; set; } public Person Person { get; set; } }
注意,这里User没有UserId
接下来上mapping:
public PersonMap() { this.ToTable("PersonTable"); this.HasKey(t => t.PersonId); this.Property(t => t.PersonId).HasColumnName("PersonId"); this.Property(t => t.Gender).HasColumnName("Gender").IsRequired(); this.Property(t => t.LastName).HasColumnName("LastName").IsRequired(); this.Property(t => t.GivenName).HasColumnName("GivenName").IsRequired(); this.Property(t => t.MiddleName).HasColumnName("MiddleName");//默认是IsOptional() }
public UserMap() { this.ToTable("UserTable"); this.HasKey(t => t.PersonId); this.Property(t => t.UserName).HasColumnName("UserName"); this.Property(t => t.Password).HasColumnName("Password"); this.HasRequired(t => t.Person) .WithOptional(t => t.UserAccount); }
这样生成的表,关系就是正确的。
不共享主键的方法(不推荐)
Person类一样,唯一不一样的是User类
Model:
public class User { public UserId { get; set; }//主键 public string UserName { get; set; }
public string Password { get; set; } public Person Person { get; set; } }
Mapping:
public UserMap() { this.ToTable("UserTable"); this.HasKey(t => t.UserId); this.Property(t => t.UserName).HasColumnName("UserName"); this.Property(t => t.Password).HasColumnName("Password"); this.HasRequired(t => t.Person) .WithOptional(t => t.UserAccount) .Map(t=>t.MapKey("PersonId"));; }
这样的方法,我也使用过,貌似没有出问题,但是,有3个原因我不建议使用:
1. 非标准方法,不能保证多重关系级联时不产生问题。
2. 无法使用PersonId property设置关系,而必须使用DbContext中的实体,做为参数漫天传不是一个很好的做法。一旦DbContext刷新,而没有重新刷新实体,会导致entity framwork报错。
3. 根据官方的文档,有navigation property时建议在实体中保留foreign key。
一些错误的做法(有时候也能用,但是本质上是错误的)
有一些地方,比如教你怎么使用早期entity frame版本的blog,可能会推荐你使用HasMany来mapping一对一关系,我个人是不推荐的(虽然HasMany之后可以使用Fluent API来map外键)。这是一种取巧的方法,使用一对多的方式代替了一对一,但是从本质上破坏了一对一关系的逻辑。处于代码可读和维护的角度来说,不推荐这样使用。我使用过一对多关系代替一对一使用,途中遇到过一些奇怪的问题,这里不一一阐述(其实是我没有一一记录)。
文中若有如有不妥之处,敬请批评指正。
参考文档:
【1】http://stackoverflow.com/questions/1761362/entity-framework-one-to-one-mapping-issues
【2】http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations 这篇文章里告诉了你使用共享主键方法mapping一对一关系的方法,以及“弊端”,个人觉得弊端说的不是特别有道理。但是全文对一对一关系解释的相当仔细,值得参考。
【3】http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations 这篇文章和【2】出自同一个博客,这里列出来,是因为说的很好。这里使用了HasMany来mapping“一对一关系”,但是仔细看一下,你会发现,这里在本质上其实是一对多关系的,因为需求改变了,导致数据库设计从一对一升级成为了一对多,所以这样的方法反而是正确可取的。