EF Code First 学习笔记:关系

一对多关系

项目中最常用到的就是一对多关系了。Code First对一对多关系也有着很好的支持。很多情况下我们都不需要特意的去配置,Code First就能通过一些引用属性、导航属性等检测到模型之间的关系,自动为我们生成外键。观察下面的类:

View Code
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类中有一个对Destination的引用属性,同时Destination中又有一个集合导航属性Lodgings,因此推测出DestinationLodging的关系是一对多关系,所以在生成的数据库中为自动为Lodging表生成外键:

其实,只要在一个类中存在引用属性,即:

View Code
 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; }
    } 

或一另一个类中存在导航属性:

View Code
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都能检测到它们之间一对多的关系,自动生成外键。

 指定外键

当然我们也可以自己在类中增加一个外键。默认情况下,如果你的外键命名是规范的话,Code First会将的该属性设置为外键,不再自动创建一个外键,如:

View Code
 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; }
    } 

规范命名是指符合:命名为“[目标类型的键名],[目标类型名称]+[目标类型键名称]”,或“[导航属性名称]+[目标类型键名称]”的形式,在这里目标类型就是Destination,相对应的命名就是:DestinationId,DestinationDestinationId,TargetDestinationId

对于命名不规范的列,Code First会怎做呢?

比如我们将外键改为:

public int TarDestinationId { get; set; }

再重新生成数据库:

可以看到Code First没有识别到TarDestinationId是一个外键,于是自己创建了一个外键:Target_DestinationId。这时我们要告诉Code First该属性是一个外键。

使用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位置的不同,其后带的参数也不同。这样,生成的数据库就是我们所期望的了。Code First没有再生成别的外键。

用Fluent API指定外键:

modelBuilder.Entity<Lodging>().HasRequired(p => p.Target).WithMany(l => l.Lodgings).HasForeignKey(p => p.TarDestinationId);

对同一个实体多个引用的情况

我们来考虑一下下面的情况:

View Code
 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; }
        //第一联系人
        public Person PrimaryContact { get; set; }
        //第二联系人
        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(旅店)有两个对Person表的引用,分别是PrimaryContact与SecondaryContact,同时,在Person表中也有对这两个联系人的导航:PrimaryContactFor与SecondaryContactFor。

看看Code First默认会生成怎样的数据库

天哪,竟然生成了四个外键。因为有两套类型一样的导航属性与引用属性,Code First无法确定它们之间的对应关系,就单独为每个属性都创建了一个关系。这肯定不是我们所期望的,为了让Code First知道它们之间的对应关系,在这里要用到逆导航属性来解决。

使用Data Annotations:

       //第一联系人
        [InverseProperty("PrimaryContactFor")] 
        public Person PrimaryContact { get; set; }
        //第二联系人
        [InverseProperty("SecondaryContactFor")] 
        public Person SecondaryContact { get; set; } 

或使用Fluent API:

 modelBuilder.Entity<Lodging>().HasOstina74cC人

对同一个实体多个引用的情况

我们来考虑一下下面的情况:

tset; }

可2091106-c0fd051acf394e5fb051bb54f088f3c2有两套类型一样ng>

View Code
class Lodging { publicint PersonID { get; set; } public TarDestinationIdpublic Person SecondaryCor: #0000ff;">string Name { get; set; } public string Name { getset; } } public class Lodging { publicint PersonID { get; setpublic get; sate{;">setpublic get; set; } public decimal MilesFromNearestAirport { get; set; } public string FirstName { get; < style="color:RowVpan0000ff;">set; } public byte[] Photo { getset; } } public

可2095032-b745767893c1467bbe8c3fbf22dee9e="cnblogs_code">

mo于是自p>
表Activr: Trip0我仆ncl外定在Perlass性cnblogs_cron联属> 表上面 > 表中s_cstinationtblog.c"属性名称]+[目_就是 表stine="co和style="coo;的yle="于elBuilder.Ent测
   3f-a760-3c9c24b68f93')">rget).WithMany(l => l.LodginTrip个} 

我们来考虑一下econdaryCorrrrrrrr{econdaryCorrrrrrrrrrrrm.ToTes/b0000;">TarDestinationId")] public Person;econdaryCorrrrrrrrrrrrm.MapLeft#800000;">TarDestinationId")] public Person>

我们来考 public TarDestinationId")] public Person;econdaryCorrrrrrrr}p>/的引用,分别是Buildging>().HasOstina74cC人 Activr: 个}

我们来考虑一下econdaryCorrrrrrrr{econdaryCorrrrrrrrrrrrm.ToTes/b0000;">TarDestinationId")] public Person;econdaryCorrrrrrrrrrrrm.MapLeft#800000;">TarDestinationId")] public ")] public Person>econdaryCorrrrrrrr}p>/的引用,分别是ng> <外鮚多关系de First对一寧设oa Annotat

是测刨到外鮚多关中乙act,是规

测刌TarDestincode_show('4519ce26-151c-473f-a760-3c9c24b68f93')">View Code
class Lodging { public int PersonID { get; set; } public int PersonID { get; set; } public bool IsResort { get; set; } public string LastName { get; set; } public int TarDestinationId { getset; } public byte[] Photo { set; } public List<Lodginle="color: #0000ff;">class Lodging { publicint TarDestinationId { get; set; } public int PersonID { get; set; } public List<Lodging> Lodgings { getget; set; } public byte[] Photo { set; } } public 创建了一与式BreakAway.an sty91af;">/p>

on联erlas体端irst须er.Ent关 Builder.En 或/20130注释显式测此on联erlas体端认情况无泪属性都创建了认哪de_ t须er.EntBuilder.Entn style="color: 进皘示测 pre> n style="color: prstyl>26-151c-473f-a760-3c9c24b68f93')">View Code

class Destination { publpan> int PersonID { get; set; } public byte[] Photo { get; set; } public bool IsResort { get; set; } public string LastName { get; set; } public int TarDestinationId { getset; } public byte[] Photo { set; } public List<Lodginle="color: #0000ff;">class Lodging { publicTarDestinationId")] public Persontyle="color: #0000ff;">int TarDestinationId { get; set; } public int PersonID { get; set; } public List<Lodging> Lodgings { getget; set; } public byte[] Photo { set; } } public an stye#0009oreignKey(p => p.TarDestie#000Of).情";().Hasp.TarDestie#000/strong>

我们or: #0000ff;">publi键名称中增其t默n stye#000表中eror: #000既cogs_c你翅须 中增pre>p>

.HasOstina74learspp>

.HasblogcC_post_info_b .Hasbloge

.HasblogE#000Tagspp>

.HasblogcC_post_info">

.HasOstina74learspp>

.Hasblogpost_next_ongvspp>

>

>> >

.HasOstin = gpostcolospaosted @">; } blogpost-date">

-01-22 11:34getGyoung 阅读(>; } blogpost_v728人unt">..."color:n 评论(>; } blogpost_"cnm<人unt">..."color:n aoa href f" alts="" an class="cnbEditPosts.aspx?postblo2869782icrel="nof486ow">编辑 oa href="#vent)" src=AddToWz(2869782);return fals,ev>收藏>

>

lor: # typ0fftext/javalor: #">var id6owCcnm<s=true,cb_b /or: #p> >> > a>.HasblogcC-"cnm<s-placehotionspp> var "cnm<Manag000= new cCCcnm<Manag00();"cnm<Manag00.re#ionCcnm<s(0)>/or: #p>.Hasblo'"cnm<form'sOstina'"cnm<form'p>a n000='"cnm<form'pp>a>>.Hasblo'.HaCcnm<Show'pp>

.Hasblo'"cnm<nav'>>; } blo'; } _refresh_tips'?

a href='javalor: #:void(0)>'ent)" src'return RefreshCcnm<">g()>'eblo'lnk_RefreshCcnm<s' ru ato';erver'sOsildeidrDes='Scoloc'>刷新评论p>a>a href='#'ent)" src'return RefreshPag0()>'>刷新页面p>a>a href='#top'>返回顶部>

.Hasblo'"cnm<form人ntainer'pp>

.HasOstina'ad_text_"cnm<box'eblo'ad_text_u#ion_"cnm<box'>>

.Hasblo'ad_t2'>>

.Hasblo'opt_u#ion_post'pp>

.Hasblo'"cC1'sOstina'"_ad_b

.Hasblo'u#ion_post_news'pp>

.Hasblo'"cC2'sOstina'"_ad_b

.Hasblo'u#ion_post_kb'pp>

.Hasblo'H">goryToday'sOstina'"_ad_b

lor: # typ0f'text/javalor: #'an stylfixPostBody(n>econdan sTi00out(func#0000()0ffincrem<-728r: #0(cb_e#000Il); }, 50n>econdaestiverAdT2(n>econdaestiverAdC1(n>econdaestiverAdC2(n>ondaecondaloadNewsAndKb(n>econdaloadB econdaLoadPostInfoe econdaGetPngvNextPost(cb_e#000Il, cb_b econdaload";U#ionPost(n>econdaGetH">goryToday(cb_b ond>>or: #p>>

>> > > > .HasblogsideBarsp> .HasblogsideBarMainsp> >!--done--p>.HasOstina7newsItem">gTitle">公告> .HasblogcC-newsspp> loadB /or: #p>>

> .HasblogcC-cale"cold1-a924-71a31d04164',spp> loadB /or: #p> > .Hasblogleftc860<ntainersp> .HasblogcC-side;">umnspp> loadB umn()>/or: #p> >

> > > .HasOstina74learspp>

> .HasOstina74learspp>

.Hasblogfootersp> >!--done--p>Copyright ©

7 Gyoung> > > window.tc#0p486fig0= tyle="colorscolocPngfix:lor" alt=""scoloc.tc#0p="cn",yle="colorbutt#00/>g:{econdaryCorrrralip0418{qrimg: " alt="" />>or: #p>lor: # .gif" alt=""scoloc.tc#0p="cn/js/tc#0p=min"jsspp>or: #p>.Hasblo")" sri_wid外3692icpp> >bodyp>>htmlp>