表之间的关系分为一对多,多对多,一对一三种,实质就是对外键进行配置。
一、一对多
1. Required
Destination包含Lodging>的集合。
public class Destination { public int DestinationId { get; set; } public string Name { get; set; } public string Country { get; set; } public string Description { get; set; } public byte[] Photo { get; set; } public List<Lodging> Lodgings { get; set; } } public class Lodging { public int LodgingId { get; set; } public string Name { get; set; } public string Owner { get; set; } public bool IsResort { get; set; } public decimal MilesFromNearestAirport { get; set; } public Destination Destination { get; set; } }
这样,EF会自动给Lodging表生成外键。Destination_LodgingId.
注意到这个时候Fk,是可以为Null的。修改Lodging,给Destination加上Required。
[Required] public Destination Destination { get; set; }
再运行
注意到Fk是not null了。
用Api的方式:
modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithOptional(l => l.Destination);
在配置表关系的时候,has方法多是和With方法一起搭配使用。
• HasOptional //可为null
• HasRequired //不为null
• HasMany //集合
has表示主体对从体的关系。.HasMany(d => d.Lodgings) 表示Destination拥有Lodgings的集合。
• WithOptional //可为null
• WithRequired //不可为null
• WithMany //集合
with表示从体对主体。.WithOptional(l => l.Destination); 表示Lodging的Destination是可以为null的。
可以给Lodging加一个外键。
public int DestinationId { get; set; }
有了外键,我们赋值的时候附一个id就行了,而不用赋一个对象。
外键DestinationId为非null,是因为他是int型的,如果换成Nullable<int>,数据库会将其设置为可null。
2.Foreignkey
有时候我们属性命名并不规范,如下。这个时候EF就不知道AccommodationId是我们指定的外键如果没有特性[Foreignkey("name")]
[ForeignKey("Accommodation")] public int AccommodationId { get; set; } public Lodging Accommodation { get; set; } //.....或..... public int AccommodationId { get; set; } [ForeignKey("AccommodationId")] public Lodging Accommodation { get; set; }
API:
modelBuilder.Entity<InternetSpecial>().HasRequired(s => s.Accommodation).WithMany(l => l.InternetSpecials)
同样在2中,我们删掉Lodging中的Destination和DestinationId,加上LocationId。
// public Destination Destination { get; set; } // public int DestinationId { get; set; } public int LocationId { get; set; }
要指定LocationId为外键可以:
修改Destination
[ForeignKey("LocationId")] public List<Lodging> Lodgings { get; set; }
Api:
modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithRequired().HasForeignKey(l => l.LocationId);
3.InverseProperty
再复杂些,Person类拥有两个Lodging集合。
public class Person { public Person() { Address=new Address(); Info=new PersonalInfo() { Weight = new Measurement(), Height = new Measurement() }; } [Key] public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public PersonalInfo Info { get; set; } public Address Address { get; set; } public PersonPhoto Photo { get; set; } public List<Lodging> PrimaryContactFor { get; set; } public List<Lodging> SecondaryContactFor { get; set; } } public class Lodging { public int LodgingId { get; set; } [Required] [MaxLength(200)] [MinLength(10)] public string Name { get; set; } [StringLength(200, MinimumLength = 2)] public string Owner { get; set; } public bool IsResort { get; set; } // public Destination Destination { get; set; } // public int DestinationId { get; set; } public List<InternetSpecial> InternetSpecials { get; set; } public Person PrimaryContact { get; set; } public Person SecondaryContact { get; set; } }
这个时候直接生产的表是:
这样就乱掉了。EF搞不清。需用用InverseProperty告诉它
[InverseProperty("PrimaryContactFor")] public Person PrimaryContact { get; set; } [InverseProperty("SecondaryContactFor")] public Person SecondaryContact { get; set; }
然后生成的外键就干净了。(SocialSecurityNumber是person的主键)
4.级联删除。
在数据库中,因为外键会在实体删除的时候触发级联删除。我们有时候需要配置这个功能关闭。且只有API的方式配置
HasRequired(l=>l.Destination).WithMany(d=>d.Lodgings).WillCascadeOnDelete(false)
如果有多个Required指向同一张表,有的数据库是不支持多个关系的级联删除,这个时候也需要关闭这个功能。
二、多对多
1.Trip和Activity相互包含彼此的集合。
public class Trip { [Key] public Guid Identifier { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public decimal CostUSD { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public decimal MilesFromNearestAirport { get; set; } public List<Activity> Activities { get; set; } } public class Activity { public int ActivityId { get; set; } [Required, MaxLength(50)] public string Name { get; set; } public List<Trip> Trips { get; set; } }
生成了三张表:
可以再修改第三张表的表名。TripConfiguration:
HasMany(t => t.Activities).WithMany(a => a.Trips).Map(c => c.ToTable("TripActivities"));
进而也可以修改这个表的键
HasMany(t => t.Activities) .WithMany(a => a.Trips) .Map(c => { c.ToTable("TripActivities"); c.MapLeftKey("TripIdentifier"); c.MapRightKey("ActivityId"); });
进行一个查询:
var tripWithActivities = context.Trips.Include("Activities").FirstOrDefault();
三、一对一
如果两个对象,单独相互依赖,这个时候就需要指定谁依赖谁。不然EF不知道就会报错。比如PersonPhoto和Person是一对一的关系。
public class Person { public Person() { Address=new Address(); Info=new PersonalInfo() { Weight = new Measurement(), Height = new Measurement() }; } [Key] public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public PersonalInfo Info { get; set; } public Address Address { get; set; } public PersonPhoto Photo { get; set; } public List<Lodging> PrimaryContactFor { get; set; } public List<Lodging> SecondaryContactFor { get; set; } } public class PersonPhoto { [Key] [ForeignKey("PhotoOf")] public int PersonId { get; set; } public byte[] Photo { get; set; } public string Caption { get; set; } public Person PhotoOf { get; set; } }
没有[Required]不然会出现下面的错误,EF它不能擅自决定。
以上都是这一章的笔记。
园友的同类型博客