LINQ to Entities works in a manner that is similar to that of LINQ to SQL . You define a model to represent your application domain. You then create a map between this model and your actual data source. You can then load data into the model and query against it by using LINQ. However, LINQ to Entities uses the ADO.NET Entity Framework as a basis for the models. These models can be created for any data source (not just for SQL Server).
The ADO.NET Entity Framework allows you to create a conceptual model of your application domain. This ensures that you write application code against the conceptual domain objects rather than directly against a relational database. This allows your model and database to evolve independently. If changes need to be made to either, you only need to change the mappings. In this way, you can even change your storage schemas without having to change your actual application code. You would just change the mapping.
Creating an Entity Model
You create an Entity Framework model by using the ADO.NET Entity Data Model template from the Add New Item dialog box in Visual Studio. This file is an .edmx file. Like a LINQ to SQL DBML file, this file has an XML representation of the schema
精簡後的實體資料模型內容 (Entity Data Model , *.edmx)
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace Northwind_EDMX
{
public partial class NorthwindEntities : ObjectContext
{
#region AddTo 方法
public void AddToOrder_Details(Order_Details order_Details)
{
base.AddObject("Order_Details", order_Details);
}
public void AddToOrders(Orders orders)
{
base.AddObject("Orders", orders);
}
public void AddToProducts(Products products)
{
base.AddObject("Products", products);
}
#endregion
}
public partial class Customers : EntityObject
{ }
public partial class Employees : EntityObject
{ }
public partial class Order_Details : EntityObject
{ }
public partial class Orders : EntityObject
{ }
}
Note
這個工具還有其他的功能,例如:由模型反向建立資料庫、與資料來源比對驗證模型的對應資訊、或者建立 stored procedure 對應等。 不過部份功能僅含在 Visual Studio Feature Pack 2之中。
Writing LINQ to Entities Queries
實體模型(entity model)是由 System.Data.Objects.ObjectContext類別繼承而來的。 它提供以物件的方法來查詢或使用實體資料。
底下範例示範如何使用 LINQ to Entities執行 CRUD 。
查詢
NorthwindEntities db = new NorthwindEntities();
var query = from emp in db.Employees
where emp.FirstName.Length > 5
select emp;
gv1.DataSource = query;
gv1.DataBind();
新增
NorthwindEntities db = new NorthwindEntities();
var emp = new NorthwindDB.Employees();
emp.FirstName = "vito";
emp.LastName = "shao";
emp.Age = 40;
db.Employees.AddObject(emp);
db.SaveChanges();
修改
當變更模型中資料,實際上變更的是記憶體中的資料,若要保存(Persists)這些變更,必須呼叫 SaveChanges方法,才會將異動回存至資料來源。
實體物件有一個 EntityState屬性,用來記錄目前的狀態,它是由 EntityState列舉值所構成: 回存動作就是依這個狀態值來判斷該回存哪些實體物件。
- Detached :(1)當該物件被加入至Context之前。
- Unchanged :(2)當該物件被載入Context中,或從上一次呼叫 SaveChanges之後都沒有修改過。
- Added :(3)當透過呼叫 AddObject將物件加入至Contex中。
- Deleted :(4)當透過呼叫 DeleteObject將物件由Contex中移除。
- Modified :(16)當此物件已變更,但是尚未呼叫 SaveChanges方法。
當叫用 SaveChanges方法時,只有處於 Added、Modified 或 Deleted 狀態的物件會被更新。
要回存物件中的資料時,可能有以下幾種狀況,必須使用不同的方法處理:
連接情況下
連接情況指的是在同一個 Context 之中,修改由資料庫中檢索出來的實體。 這種狀況只要在修改屬性值之後,再叫用 SaveChanges方法即可。
using (AspNetDB_ObjectContext context = new AspNetDB_ObjectContext())
{
var query = from user in context.Users
where user.UserId >= 2 && user.UserId <= 5
select user;
foreach (var user in query)
{
if ((user.UserId % 2) == 0)
user.LastActivityDate = DateTime.Now;
}
context.SaveChanges();
}
上面這個例子,假如符合條件的資料共有四筆,當呼叫 SaveChanges更新時,只會更新二筆。 而且,因為我們只更動LastActivityDate這個欄位,所以只有這個欄位的狀態是已變更,最後它對資料庫的操作也只會更新這個欄位,其他欄位都不會處理。
斷開連接情況下
斷開連接情況指的是要處理的物件不在該 Context 之中。這種狀況有以下幾種處理方法:
使用 AttachTo 方法
AttachTo方法會將具有實體索引鍵的物件附加至物件內容,之後若有任何資料變更,必須呼叫 SaveChanges回存。
注意,叫用 AttachTo的時機很重要,因為當叫用 AttachTo時,之前的任何變更,都不會列入追縱的,只有在叫用方法後的變更,才會列入追縱的。 所以下面例子中的 FirstName 都不會被寫入。
using (ExtendedAspNetDB context = new ExtendedAspNetDB())
{
EntityUsers user = new EntityUsers();
user.UserId = 2;
user.UserName = "vito";
user.LastActivityDate = DateTime.Now;
context.AttachTo("Users", user); //在這之前所設定的值,都不會被寫入
user.UserName = "vito2";
context.SaveChanges();
}
上面程式碼,最後只執行以下操作:
update [Users] set [UserName] = 'vito2' where UserId=2
使用 Attach 方法
Attach方法會將具有實體索引鍵的物件附加至物件內容,再利用 ChangeObjectState方法變更狀態,然後呼叫 SaveChanges回存。
using (ExtendedAspNetDB context = new ExtendedAspNetDB())
{
EntityUsers user = new EntityUsers();
user.UserId = 2;
user.UserName = "vito";
user.LastActivityDate = DateTime.Now;
context.Users.Attach(user);
context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);
context.SaveChanges();
}
上面程式碼,最後執行了以下操作:
update Users set UserName='vito',LastActivityDate='2013-02-22 12:59:28.457' where UserId=2
這方法有個很大的問題要注意,在呼叫 ChangeObjectState方法時,是所有欄位都會當做是已變動欄位。 也就是,若部份欄位你都沒有設定到,也就是沒給定值,資料庫就可能就被更新成空白。
使用 ApplyCurrentValues 方法
使用這個方法,必須先查詢出資料庫中要修改的實體,然後呼叫 ApplyCurrentValues方法修改實體的值。
using (ExtendedAspNetDB context = new ExtendedAspNetDB())
{
EntityUsers user = new EntityUsers();
user.UserId = 2;
user.UserName = "vito2";
context.Users.OfType().First(u => u.UserId == user.UserId);
context.Users.ApplyCurrentValues(user);
context.SaveChanges();
}
上面程式碼,最後執行了以下操作,很神奇吧。 因為它會將參數中的實體和查詢出來的實體做欄位值的比對,然後產生最佳化的SQL,也就是只會更新不同值的欄位。 這個例子,因為資料庫中的 UserName 原本就是 vito2 ,所以不會再被 Update , 資料庫中的 LastActivityDate 欄位原本是有值的,但是參數中的實體的LastActivityDate卻是空的,所以會被 Update。
update Users set LastActivityDate='' where UserId=2
批次更新資料
更新多筆資料時,除了可以使用迴圈逐筆更新之外。
如果想要一次更新多筆,也可以叫用 ExecuteStoreCommand方法,直接透過指令更新。
NorthwindEntities context = new NorthwindEntities();
context.ExecuteStoreCommand("Update Employees Set City='New Taipei' Where City='Taipei'");
刪除
連接情況下
NorthwindEntities db = new NorthwindEntities();
var oneEmp = db.Employees.First(emp => emp.FirstName == "vito" && emp.LastName == "shao");
db.Employees.DeleteObject(oneEmp);
db.SaveChanges();
斷開連接情況下
NorthwindEntities db = new NorthwindEntities();
NEmployees emp = new NEmployees();
emp.EmployeeID = 11;
db.AttachTo("Employees", emp);
db.DeleteObject(emp);
db.SaveChanges();