1. 程式人生 > >Entity Framework 基礎

Entity Framework 基礎

sea port runt ext2 byte start 4.5 byte[] 生命

一、什麽是Entity Framework

微軟官方提供的ORM工具,ORM讓開發人員節省數據庫訪問的代碼時間,將更多的時間放到業務邏輯層代碼上。EF提供變更跟蹤、唯一性約束、惰性加載、查詢事物等。開發人員使用Linq語言,對數據庫操作如同操作Object對象一樣省事。

EF有三種使用場景,1. 從數據庫生成Class,2.由實體類生成數據庫表結構,3. 通過數據庫可視化設計器設計數據庫,同時生成實體類。

技術分享

O/RM是什麽?

ORM 是將數據存儲從域對象自動映射到關系型數據庫的工具。ORM主要包括3個部分:域對象、關系數據庫對象、映射關系。ORM使類提供自動化CRUD,使開發人員從數據庫API和SQL中解放出來。

二、Entity Framework 架構

技術分享

EDM (實體數據模型):EDM包括三個模型,概念模型、 映射和存儲模型。

概念模型 ︰ 概念模型包含模型類和它們之間的關系。獨立於數據庫表的設計。

存儲模型 ︰ 存儲模型是數據庫設計模型,包括表、 視圖、 存儲的過程和他們的關系和鍵。

映射 ︰ 映射包含有關如何將概念模型映射到存儲模型的信息。

LINQ to Entities ︰ LINQ to Entities 是一種用於編寫針對對象模型的查詢的查詢語言。它返回在概念模型中定義的實體。

Entity SQL: Entity SQL 是另一種爐類似於L2E的言語,但相給L2E要復雜的多,所以開發人員不得不單獨學習它。

Object Services(對象服務):是數據庫的訪問入口,負責數據具體化,從客戶端實體數據到數據庫記錄以及從數據庫記錄和實體數據的轉換。

Entity Client Data Provider:主要職責是將L2E或Entity Sql轉換成數據庫可以識別的Sql查詢語句,它使用Ado.net通信向數據庫發送數據可獲取數據。

ADO.Net Data Provider:使用標準的Ado.net與數據庫通信

三、Entity Framework運行環境

EF5由兩部分組成,EF api和.net framework 4.0/4.5,而EF6是獨立的EntityFramework.dll,不依賴.net Framework。使用NuGet即可安裝EF。

技術分享 技術分享

四、創建實體數據模型

使用向導創建實體類,或鍵添加,傻瓜式的~

技術分享

添加完成之後,.config文件中會添加以下配置

<?xmlversion="1.0"?><configuration><configSections><!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --><sectionname="entityFramework"type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"requirePermission="false"/></configSections><startup><supportedRuntimeversion="v4.0"sku=".NETFramework,Version=v4.5"/></startup><entityFramework><defaultConnectionFactorytype="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"/><providers><providerinvariantName="System.Data.SqlClient"type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/></providers></entityFramework><connectionStrings><addname="SchoolDBEntities"connectionString="metadata=res://*/SchoolDB.csdl|res://*/SchoolDB.ssdl|res://*/SchoolDB.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\sqlexpress;initial catalog=SchoolDB;integrated security=True;multipleactiveresultsets=True;application name=EntityFramework&quot;"providerName="System.Data.EntityClient"/></connectionStrings></configuration>

Context & Entity 類:

每個Entity Data Model 生成一個context類,類數據庫每個表生成一個entity類。如在School.edmx中包含的兩個重要的文件{EDM Name}.context.tt和{EDM Name}.tt:

技術分享

School.Context.tt:T4模板用於生成的context類,可以從目錄結構中看到School.Context.tt下包含一個School.Context.cs文件。

School.tt:用於生成表映射的實體類。Entity類是POCO類。如Student生成

publicpartialclassStudent

{

public Student()

{

this.Courses = new HashSet<Course>();

}

publicint StudentID { get; set; }

publicstring StudentName { get; set; }

publicNullable<int> StandardId { get; set; }

publicbyte[] RowVersion { get; set; }

publicvirtualStandard Standard { get; set; }

publicvirtualStudentAddress StudentAddress { get; set; }

publicvirtualICollection<Course> Courses { get; set; }

}

五、模板瀏覽器

以SchoolDB為例,切換到Model View視圖下,看到類圖結構:

技術分享

六、DBContext

第四節中提到EDM生成SchoolDBEntities類,該類從System.Data.Entity.DbContext類繼承。EntityFramework4.1中Context類從ObjectContext類繼承。DbContext類與ObjectContext類似,它對ObjcetContext類進行包裝更利於開發的三種模式:CodeFirst、Model First、Database First.

DbContext是EntityFramework很重要的部分,連接域模型與數據庫的橋梁,是與數據庫通信的主要類。

技術分享

DbContext主要負責以下活動:

EntitySet:DbContext包含了所有映射到表的entities

Querying將Linq-To-Entities轉譯為Sql並發送到數據庫

Change Tracking從數據庫獲取entities後保留並跟蹤實體數據變化

Persisting Data根據entity狀態執行Insert、update、delete命令

CachingDbContext的默認第一級緩存,在上下文中的生命周期中存儲entity

Manage RelationshipDbContext在DbFirst模式中使用CSDL、MSL、SSDL管理對象關系,Code first中使用fluent api 管理關系

Object MaterializationDbContext將物理表轉成entity實例對象

DEMO

DbContext實例化:

using (var ctx = newSchoolDBEntities())
{
//Can perform CRUD operation using ctx here..
}

將DbContext轉為ObjectContext

using (var ctx = newSchoolDBEntities())
{
var objectContext = (ctx as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext;
//use objectContext here..
}

七、Entity Framework中的Entity類型

POCO Entity (Plain Old CLR Object):

不依賴於任何Framework的類的類(also known as persistence-ignorant objects),為Entity Data Model生成CRUD命令服務。

public class Student
{

public Student()

{

this.Courses = new List<Course>();

}

public int StudentID { get; set; }

public string StudentName { get; set; }

public Nullable<int> StandardId { get; set; }

public Standard Standard { get; set; }

public StudentAddress StudentAddress { get; set; }

public IList<Course> Courses { get; set; }

}

Dynamic Proxy (POCO Proxy):

Dynamic Proxy是運行時POCO類的代理類,類似POCO類的包裝。Dynamic Proxy允許延遲加載(Lazy loading),自動跟蹤更改。POCO Entity必需滿足以下幾點才能轉為POCO Proxy:

1. 必需聲明為public 類

2. 不可以是sealed類

3. 不可以是抽象類

4. 導航屬性必需是public,vitual(Entity包含兩種屬性,標量屬性Scalar properties:Entity本身的字段值,Navigation properties:其它entity的引用如班級-學生)

5. 集合屬性必需是 ICollection<T>

6. ProxyCreationEnabled 選項必需是true

public class Student

{

public Student()

{

this.Courses = new HashSet<Course>();

}

public int StudentID { get; set; }

public string StudentName { get; set; }

public Nullable<int> StandardId { get; set; }

public virtual Standard Standard { get; set; }

public virtual StudentAddress StudentAddress { get; set; }

public virtual ICollection<Course> Courses { get; set; }

}

八、Entity Relationships:

技術分享

九、 Entity Lifecycle

在我們做CRUD操作時,要先了解EntityFramework如何管理實體狀態。每個實體的生命周期內都會在DbContext上下文中保存一個狀態,分別是

Added Deleted Modified Unchanged Detached

技術分享

十、Code First、DBFirst、Model First

CodeFirst 領域設計時先定義實體類,用實體類生成數據庫

DbFirst 從數據庫生成實體類

Model First 使用Visual Studio實體設計器,設計ER,同時生成Entity類和DB

技術分享

十一、使用查詢

三種查詢方式1) LINQ to Entities, 2) Entity SQL, and 3) Native SQL

LINQ to Entities:

LINQ Method syntax:

//Querying with LINQ to Entities

using (var context = newSchoolDBEntities())

{

var L2EQuery = context.Students.where(s => s.StudentName == "Bill");

var student = L2EQuery.FirstOrDefault<Student>();

}

LINQ Query syntax:

using (var context = new SchoolDBEntities())

{

var L2EQuery = from st in context.Students

where st.StudentName == "Bill"select st;

var student = L2EQuery.FirstOrDefault<Student>();

}

Entity SQL:

//Querying with Object Services and Entity SQL

string sqlString = "SELECT VALUE st FROM SchoolDBEntities.Students " +

"AS st WHERE st.StudentName == ‘Bill‘";

var objctx = (ctx as IObjectContextAdapter).ObjectContext;

ObjectQuery<Student> student = objctx.CreateQuery<Student>(sqlString);

Student newStudent = student.First<Student>();

//使用EntityDataReader

using (var con = newEntityConnection("name=SchoolDBEntities"))

{

con.Open();

EntityCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT VALUE st FROM SchoolDBEntities.Students as st where st.StudentName=‘Bill‘";

Dictionary<int, string> dict = newDictionary<int, string>();

using (EntityDataReader rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess |CommandBehavior.CloseConnection))

{

while (rdr.Read())

{

int a = rdr.GetInt32(0);

var b = rdr.GetString(1);

dict.Add(a, b);

}

}

}

Native SQL:

using (var ctx = newSchoolDBEntities())

{

var studentName = ctx.Students.SqlQuery("Select studentid, studentname, standardId from Student where studentname=‘Bill‘").FirstOrDefault<Student>();

}

十二、跟蹤變更與持久化場景

在連接狀態下持久化與脫機狀態下持久化

連機狀態下持久化,在同一個DbContext中不需要銷毀Entity,直接寫入數據庫

技術分享

脫機狀態持久化指讀取和保存Entity在兩個不同的DbContext中,Context2不知道Entity的更新狀態,所以必需通知Context2當前的Entity做了何種更新。

技術分享

Context只在DbSet上跟蹤添加和刪除

正確的添加和刪除

using (var context = new SchoolDBEntities())

{

var studentList = context.Students.ToList<Student>();

//Perform create operation

context.Students.Add(newStudent() { StudentName = "New Student" });

//Perform Update operationStudent studentToUpdate = studentList.Where(s => s.StudentName =="student1").FirstOrDefault<Student>();

studentToUpdate.StudentName = "Edited student1";

//Perform delete operation

context.Students.Remove(studentList.ElementAt<Student>(0));

//Execute Inser, Update & Delete queries in the database

context.SaveChanges();

}

以下代碼在List中添加和刪除不起作用,只有更生有效

using (var context = new SchoolDBEntities())

{

var studentList = context.Students.ToList<Student>();

//Add student in list

studentList.Add(new Student() { StudentName = "New Student" });

//Perform update operationStudent studentToUpdate = studentList.Where(s => s.StudentName =="Student1").FirstOrDefault<Student>();

studentToUpdate.StudentName = "Edited student1";

//Delete student from listif (studentList.Count > 0)

studentList.Remove(studentList.ElementAt<Student>(0));

//SaveChanges will only do update operation not add and delete

context.SaveChanges();

}

脫機實體

脫機實體管理要先附加到Context

技術分享

//disconnected entity graphStudent disconnectedStudent = newStudent() { StudentName = "New Student" };

disconnectedStudent.StudentAddress = newStudentAddress() { Address1 = "Address", City = "City1" };

using (var ctx = newSchoolDBEntities())

{

//attach disconnected Student entity graph to new context instance - ctx

ctx.Students.Attach(disconnectedStudent);

// get DbEntityEntry instance to check the EntityState of specified entity

var studentEntry = ctx.Entry(disconnectedStudent);

var addressEntry = ctx.Entry(disconnectedStudent.StudentAddress);

Console.WriteLine("Student EntityState: {0}",studentEntry.State);

Console.WriteLine("StudentAddress EntityState: {0}",addressEntry.State);

}

添加多個關系實體時與添加單個實體一樣,更新關系實體時需要跟蹤每個實體的狀態。

十三 Entity Framework並發處理

添加RowVersion,類型為TimeStamp字段,在EDM中X修改並發屬性為Fixed。EF更新實體時會先檢查RowVersion,如果發現RowVersion不一致,則拋出DbUpdateConcurrencyException異常

技術分享

十四 貪婪加載、惰性加載與定向加載

貪婪加載:使用Include(),自動加載關聯實體

using (var context = new SchoolDBEntities())

{

var res = (from s in context.Students.Include("Standard")

where s.StudentName == "Student1"

select s).FirstOrDefault<Student>();

}

執行Sql

SELECTTOP (1)

[Extent1].[StudentID] AS [StudentID],

[Extent1].[StudentName] AS [StudentName],

[Extent2].[StandardId] AS [StandardId],

[Extent2].[StandardName] AS [StandardName],

[Extent2].[Description] AS [Description]

FROM [dbo].[Student] AS [Extent1]

LEFTOUTERJOIN [dbo].[Standard] AS [Extent2] ON [Extent1].[StandardId] = [Extent2].[StandardId]

WHERE‘Student1‘ = [Extent1].[StudentName]

惰性加載:延遲加載對象關聯的實體,用到時再加載,EF默認為LazyLoading

using (var ctx = newSchoolDBEntities())

{

//Loading students onlyIList<Student> studList = ctx.Students.ToList<Student>();

Student std = studList[0];

//Loads Student address for particular Student only (seperate SQL query)

StudentAddress add = std.StudentAddress;

}

定向加載:Reference()和Collection() 方法

using (var context = new SchoolDBEntities())

{

//Loading students only

IList<Student> studList = context.Students.ToList<Student>();

Student std = studList.Where(s => s.StudentID == 1).FirstOrDefault<Student>();

//Loads Standard for particular Student only (seperate SQL query)

context.Entry(std).Reference(s => s.Standard).Load();

//Loads Courses for particular Student only (seperate SQL query)

context.Entry(std).Collection(s => s.Courses).Load();

}

十五:執行SQL

返回實體

using (var ctx = newSchoolDBEntities())

{

//列名必需要Entity屬性匹配

var studentList = ctx.Students.SqlQuery("Select * from Student").ToList<Student>();

}

返回非實體類型

using (var ctx = newSchoolDBEntities())

{

//Get student name of string typestring studentName = ctx.Database.SqlQuery<string>("Select studentname

from Student where studentid=1").FirstOrDefault<string>();

}

執行SQL命令

using (var ctx = new SchoolDBEntities())

{

//Update commandint noOfRowUpdated = ctx.Database.ExecuteSqlCommand("Update student

set studentname =‘changed student by command‘ where studentid=1");

//Insert commandint noOfRowInserted = ctx.Database.ExecuteSqlCommand("insert into student(studentname)

values(‘New Student‘)");

//Delete commandint noOfRowDeleted = ctx.Database.ExecuteSqlCommand("delete from student

where studentid=1");

}

---

--

Entity Framework 基礎