一對(duì)多關(guān)系項(xiàng)目中最常用到的就是一對(duì)多關(guān)系了。Code First對(duì)一對(duì)多關(guān)系也有著很好的支持。很多情況下我們都不需要特意的去配置,Code First就能通過一些引用屬性、導(dǎo)航屬性等檢測(cè)到模型之間的關(guān)系,自動(dòng)為我們生成外鍵。觀察下面的類:
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; } } Code First觀察到Lodging類中有一個(gè)對(duì)Destination的引用屬性,同時(shí)Destination中又有一個(gè)集合導(dǎo)航屬性Lodgings,因此推測(cè)出Destination與Lodging的關(guān)系是一對(duì)多關(guān)系,所以在生成的數(shù)據(jù)庫(kù)中為自動(dòng)為L(zhǎng)odging表生成外鍵:
其實(shí),只要在一個(gè)類中存在引用屬性,即:
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 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; } } 或一另一個(gè)類中存在導(dǎo)航屬性:
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; } } Code First都能檢測(cè)到它們之間一對(duì)多的關(guān)系,自動(dòng)生成外鍵。 指定外鍵 當(dāng)然我們也可以自己在類中增加一個(gè)外鍵。默認(rèn)情況下,如果你的外鍵命名是規(guī)范的話,Code First會(huì)將的該屬性設(shè)置為外鍵,不再自動(dòng)創(chuàng)建一個(gè)外鍵,如:
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 int TargetDestinationId { get; set; } public Destination Target { get; set; } } 規(guī)范命名是指符合:命名為“[目標(biāo)類型的鍵名],[目標(biāo)類型名稱]+[目標(biāo)類型鍵名稱]”,或“[導(dǎo)航屬性名稱]+[目標(biāo)類型鍵名稱]”的形式,在這里目標(biāo)類型就是Destination,相對(duì)應(yīng)的命名就是:DestinationId,DestinationDestinationId,TargetDestinationId
對(duì)于命名不規(guī)范的列,Code First會(huì)怎做呢? 比如我們將外鍵改為: public int TarDestinationId { get; set; } 再重新生成數(shù)據(jù)庫(kù):
可以看到Code First沒有識(shí)別到TarDestinationId是一個(gè)外鍵,于是自己創(chuàng)建了一個(gè)外鍵:Target_DestinationId。這時(shí)我們要告訴Code First該屬性是一個(gè)外鍵。 使用Data Annotations指定外鍵: [ForeignKey("Target")] public int TarDestinationId { get; set; } public Destination Target { get; set; } 或 public int TarDestinationId { get; set; } [ForeignKey("TarDestinationId")] public Destination Target { get; set; } 注意ForeignKey位置的不同,其后帶的參數(shù)也不同。這樣,生成的數(shù)據(jù)庫(kù)就是我們所期望的了。Code First沒有再生成別的外鍵。
用Fluent API指定外鍵: modelBuilder.Entity<Lodging>().HasRequired(p => p.Target).WithMany(l => l.Lodgings).HasForeignKey(p => p.TarDestinationId); 對(duì)同一個(gè)實(shí)體多個(gè)引用的情況 我們來考慮一下下面的情況:
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 Target { get; set; } //第一聯(lián)系人 public Person PrimaryContact { get; set; } //第二聯(lián)系人 public Person SecondaryContact { get; set; } } public class Person { public int PersonID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<Lodging> PrimaryContactFor { get; set; } public List<Lodging> SecondaryContactFor { get; set; } } Lodging(旅店)有兩個(gè)對(duì)Person表的引用,分別是PrimaryContact與SecondaryContact,同時(shí),在Person表中也有對(duì)這兩個(gè)聯(lián)系人的導(dǎo)航:PrimaryContactFor與SecondaryContactFor。 看看Code First默認(rèn)會(huì)生成怎樣的數(shù)據(jù)庫(kù)
天哪,竟然生成了四個(gè)外鍵。因?yàn)橛袃商最愋鸵粯拥膶?dǎo)航屬性與引用屬性,Code First無法確定它們之間的對(duì)應(yīng)關(guān)系,就單獨(dú)為每個(gè)屬性都創(chuàng)建了一個(gè)關(guān)系。這肯定不是我們所期望的,為了讓Code First知道它們之間的對(duì)應(yīng)關(guān)系,在這里要用到逆導(dǎo)航屬性來解決。 使用Data Annotations: //第一聯(lián)系人 [InverseProperty("PrimaryContactFor")] public Person PrimaryContact { get; set; } //第二聯(lián)系人 [InverseProperty("SecondaryContactFor")] public Person SecondaryContact { get; set; } 或使用Fluent API: modelBuilder.Entity<Lodging>().HasOptional(l => l.PrimaryContact).WithMany(p => p.PrimaryContactFor); 再重新生成數(shù)據(jù)庫(kù),結(jié)果如圖:
多對(duì)多關(guān)系如果有兩個(gè)類中,各自都是導(dǎo)航屬性指向另一個(gè)類,Code First會(huì)認(rèn)為這兩個(gè)類之間是多對(duì)多關(guān)系,例如:
public class Activity { public int ActivityId { get; set; } [Required, MaxLength(50)] public string Name { get; set; } public List<Trip> Trips { get; set; } } public class Trip { public int TripId{get;set;} public DateTime StartDate{get;set;} public DateTime EndDate { get; set; } public decimal CostUSD { get; set; } public byte[] RowVersion { get; set; } public List<Activity> Activities { get; set; } } 一個(gè)Trip類可以有一些Activites日程,而一個(gè)Activity日程又可以計(jì)劃好幾個(gè)trips(行程),顯然它們之間是多對(duì)多的關(guān)系。我們看看默認(rèn)生成的數(shù)據(jù)庫(kù)是怎么樣的:
可以看到,Code First生成了一張中間表ActivityTrips,將另外兩張表的主鍵都作為外鍵關(guān)聯(lián)到了中間表上面。中間表中鍵的命名默認(rèn)為"[目標(biāo)類型名稱]_[目標(biāo)類型鍵名稱]". 指定表名 如果我們想指定中間表的名稱和鍵名稱,我們可以用Fluent API來配置。 modelBuilder.Entity<Trip>().HasMany(t => t.Activities).WithMany(a => a.Trips).Map(m =>
{
m.ToTable("TripActivities");
m.MapLeftKey("TripIdentifier");//對(duì)應(yīng)Trip的主鍵
m.MapRightKey("ActivityId");
});
或: modelBuilder.Entity<Activity>().HasMany(a => a.Trips).WithMany(t => t.Activities).Map(m =>
{
m.ToTable("TripActivities");
m.MapLeftKey("ActivityId");//對(duì)應(yīng)Activity的主鍵
m.MapRightKey("TripIdentifier");
});
一對(duì)一關(guān)系如果我們要將兩個(gè)類配置為一對(duì)一關(guān)系,則兩個(gè)類中都要配置相應(yīng)的引用屬性,如:
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public PersonPhoto Photo { get; set; } } public class PersonPhoto { [Key] public int PersonId { get; set; } public byte[] Photo { get; set; } public string Caption { get; set; } public Person PhotoOf { get; set; } } 我們?yōu)橐粋€(gè)(Person)對(duì)應(yīng)著一張相片(PersonPhoto),但如果根據(jù)這樣的模型生成數(shù)據(jù)庫(kù)為報(bào)錯(cuò): 無法確定類型“BreakAway.PersonPhoto”與“BreakAway.Person”之間的關(guān)聯(lián)的主體端。必須使用關(guān)系 Fluent API 或數(shù)據(jù)注釋顯式配置此關(guān)聯(lián)的主體端 因?yàn)镃ode First無法確認(rèn)哪個(gè)是依賴類, 使用
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public PersonPhoto Photo { 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; } } 使用Fluent API: modelBuilder.Entity<PersonPhoto>().HasRequired(p => p.PhotoOf).WithOptional(p => p.Photo); 注意:PersonPhoto表中的PersonId既是外鍵也必須是主鍵
|
|
|