C#:實體框架EF(entity framework)
本文來自:http://www.cnblogs.com/xuf22/articles/5513283.html
一、什麼是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="data source=.\sqlexpress;initial catalog=SchoolDB;integrated security=True;multipleactiveresultsets=True;application name=EntityFramework""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命令
Caching:DbContext的預設第一級快取,在上下文中的生命週期中儲存entity
Manage Relationship:DbContext在DbFirst模式中使用CSDL、MSL、SSDL管理物件關係,Code first中使用fluent api 管理關係
Object Materialization:DbContext將物理錶轉成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");
}