MVC3+EF4.1學習系列(四)----- ORM關系的處理
上篇文章 終于把基礎的一些操作寫完了 但是這些都是單表的處理 而EF做為一個ORM框架 就必須點說說對于關系的處理
處理好關系 才能靈活的運用EF
關于關系的處理 一般就是 一對一 一對多 多對多 還有就是采用雙向關聯還是單項關聯 而關系的處理 站長dudu的文章 就已經有了很好的介紹
推薦大家去看下 -------dudu的實體關系總結 這樣大家對實體關系也就有了初步的認識了 但是在dudu的文章里 一直沒有說如何處理多對多時 關系表
里有其他數據時怎么辦(這個問題曾經困擾了我好久~~ 見人就問) 這里寫下當時得到的幾種方案 也希望能跟大家探討下 好了 從實際項目開始 繼續完善我們
的demo 并在從中探討關系
先把原文中的完成后的圖貼上來 也就是我們要處理的關系

這就是這次完成后樣子 比以前多了幾個類 關系也復雜了很多 下面我來解釋這些關系
以及怎么建立的 一個一個慢慢來~~
一.創建教師實體
教師實體類
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Instructor
{
public Int32 InstructorID { get; set; }
[Required(ErrorMessage = "Last name is required.")]
[Display(Name="Last Name")]
[MaxLength(50)]
public string LastName { get; set; }
[Required(ErrorMessage = "First name is required.")]
[Column("FirstName")]
[Display(Name = "First Name")]
[MaxLength(50)]
public string FirstMidName { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Required(ErrorMessage = "Hire date is required.")]
[Display(Name = "Hire Date")]
public DateTime? HireDate { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
public virtual ICollection<Course> Courses { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
}
}
在第二篇文章里 簡單的說了下用特性進行控制與MVC的結合 這里再詳細的講解幾個
1.The Column Attribute
[Column("FirstName")]
public string FirstMidName { get; set; }
利用這個特性 我們可以使FirstMidName屬性映射到數據庫的列為FirstName 在這里 如果使用[MaxLength(10)] 可以規定字段長度 [Required]這個來規定是否允許空 這是比較常用的
2. 請大家注意這個FullName
這個是不會被創建到數據庫里的 因為它僅僅是獲得 也就是一個get 這樣是不會創建一個FullName列 在這個數據庫表里的
3.普通的多對多的關系
這篇文章是這樣 一個老師可以教多個課程 一個課程 也可以被多個老師教 與我們平時的習慣似乎有些不符合 但是尊重原文 這里依然這樣設計 于是這就是一個多對多的關系
于是有了這個導航屬性
public virtual ICollection<Course> Courses { get; set; }
4.一對一的關系
每個老師都要有一個辦公位置 而一個辦公位置 也只應該有一個老師 所以這是一個一對一的關系 用dudu的話 采用兩情相悅的方式 雙向關聯 ps:這個類還沒建呢~~
public virtual OfficeAssignment OfficeAssignment { get; set; }
二.創建辦公地點實體
辦公地點實體類
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class OfficeAssignment
{
[Key]
public int InstructorID { get; set; }
[MaxLength(50)]
[Display(Name = "Office Location")]
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
}
一對一的關系和KEY特效
繼續剛才的一對一 因為一個老師只有一個辦公地點 一個辦公地點只有一個老師 所以 這張表希望和教師表一樣 都用InstructorID做為主鍵 (就像有的時候 1對1的關系 我們放在一張表里一樣) 而通過前面的學習 我們知道 EF尋找主鍵 的規則是 命名為ID 或者命名為 類名+ID 而這里面 沒有符合要求的 遇到這種情況 我們可以通過[key] 來指定讓誰做主鍵
[Key]
public int InstructorID { get; set; }
三.修改課程實體
課程實體
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Number")]
public int CourseID { get; set; }
[Required(ErrorMessage = "Title is required.")]
[MaxLength(50)]
public string Title { get; set; }
[Required(ErrorMessage = "Number of credits is required.")]
[Range(0,5,ErrorMessage="Number of credits must be between 0 and 5.")]
public int Credits { get; set; }
[Display(Name = "Department")]
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Instructor> Instructors { get; set; }
}
}
1.The DatabaseGenerated Attribute
用這個特性是關閉自增長 然后用主鍵當課程編號 創建時需要輸入 其實這樣做我并不贊同 但是這篇文章主要是講EF 所以不糾結于細節 可能這里主要是為了講解這個特性吧
有興趣的可以看下原文~~
2.一對多的關系
因為一個院系可以開多個課程 一個課程只能屬于一個院系 所以這里是一對多關系
在這個例子請注意 我們不僅擁有 院系的導航屬性 還擁有一個導航屬性ID
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
當你的命名是這種情況時 聰明的EF 是不會在數據庫里給你建兩個列的 如果你依然擔心 可以使用 ForeignKey特效 來制定外鍵的名字
但是這里我有個疑問 我看到很多人的設計 都喜歡這樣子做 既有導航屬性 又有導航屬性ID 這樣做的意義是什么呢?方便獲取 設置初始化值么?還是什么?我用EF時 不推薦這么用
四.創建院系類
View Code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Department
{
public int DepartmentID { get; set; }
[Required(ErrorMessage = "Department name is required.")]
[MaxLength(50)]
public string Name { get; set; }
[DisplayFormat(DataFormatString="{0:c}")]
[Required(ErrorMessage = "Budget is required.")]
[Column(TypeName="money")]
public decimal? Budget { get; set; }
[DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)]
[Required(ErrorMessage = "Start date is required.")]
public DateTime StartDate { get; set; }
[Display(Name="Administrator")]
public int? InstructorID { get; set; }
public virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
}
一.The Column Attribute
[Column(TypeName="money")]
public decimal? Budget { get; set; }
通過這個特性 我們可以指定 生成到數據庫里的列的屬性
五.修改學生類
View Code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Student
{
public int StudentID { get; set; }
[Required(ErrorMessage = "Last name is required.")]
[Display(Name="Last Name")]
[MaxLength(50)]
public string LastName { get; set; }
[Required(ErrorMessage = "First name is required.")]
[Column("FirstName")]
[Display(Name = "First Name")]
[MaxLength(50)]
public string FirstMidName { get; set; }
[Required(ErrorMessage = "Enrollment date is required.")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Enrollment Date")]
public DateTime? EnrollmentDate { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}
六.修改登記表(學生課程關系表)
修改登記表
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models
{
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
[DisplayFormat(DataFormatString="{0:#.#}",ApplyFormatInEditMode=true,NullDisplayText="No grade")]
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}
七.多對多關系的思考與討論
項目的類和關系建完了 這里我最想和大家探討與分享的就是 這個如何處理多對多的關系
看我們的例子 其實有兩個多對多 一個是課程和教師 還有一個是學生和選課
課程與教師 我們是直接使用多對多 沒有為關系表建立實體類 因為我們的關系表不需要存儲其他數據
而在學生和選課這里 要存儲成績 關系表里要存儲其他的東西 這時 我們為關系表也建立了實體類 關系變成里 學生 1對多 關系表 課程也是1對多關系表
也就是說 當遇到使用多對多時 關系表還需要存儲其他東西時(本文是成績) 可以把多對多拆穿 兩個1對多 還有 實際項目不建議使用多對多來處理關系
這是這個項目給的啟事 請問大家是如何解決這種情況的呢? 所有的ORM都會遇到這種問題的吧?
他們在領域驅動群里 給了我另一種解答 直接上圖

晴天 15:23:04
分數之間不需要區分個體,所以是值對象
晴天 15:23:23
學生和課程是需要系統維護的,所以是聚合根
晴天 15:25:13
CourseMark保存針對Course的引用
晴天 15:25:28
Student下包含多個CourseMark
dax.net 關于雙向關聯
是不是雙向關聯不重要,首先要確定聚合根
班級是聚合根,學生也是聚合根 如果你的應用程序只是維護班級的基本信息,而不需要讀取這個班級到底有哪些學生,那么就不需要往班級的聚合里添加學生
相反,通常情況下,學生的一個屬性是班級,所以學生聚合里可以將班級加進去
八.關于EF的關系映射
看了這么多各種關系的類 他們之間存才的關系 多對多 一對一 一對多 處理這些映射是很麻煩的事
我們有三種方法 1.按EF默認的規則 自動生成 但是有時生成的不是我們想要的
2.通過特性的定義 來配置如何映射關系 以及如何映射到數據庫 這篇文章提到了一些
3.通過流利的API 方式來定義 這也是我推薦的方式
看這篇文章的例子
using System;
using System.Collections.Generic;
using System.Data.Entity;
using ContosoUniversity.Models;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ContosoUniversity.Models
{
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("InstructorID")
.ToTable("CourseInstructor"));
modelBuilder.Entity<Department>()
.HasOptional(x => x.Administrator);
}
}
}
依然很痛苦啊 這要多勇敢 才能把編寫model 到再編寫數據庫映射等 寫完 還有這么多api要記什么意思 實在是太痛苦了
不要怕 想用code fisrt 有干凈的poco 但又不想寫這些的 你們的福音來了 強烈推薦使用
Entity Framework Power Tools
可以根據數據庫 一鍵生成想要的model 與映射 原子中也有介紹的了 介紹連接
生成后 和我們自己寫的一樣 而且更加規范 統一的映射管理 真的很完美~~
九.總結
通過這節 終于把ORM的關系理順了 但是 這只是把關系建立起來 關系的處理遠沒結束
下一節 查找關聯數據 如何利用好延遲加載與懶惰加載

浙公網安備 33010602011771號