本文將演示如何通過 Entity Framework 數(shù)據(jù)模型創(chuàng)建、修改、刪除數(shù)據(jù)庫記錄。
為了在數(shù)據(jù)庫里創(chuàng)建新紀錄,需要創(chuàng)建相應(yīng)實體類的新實例,填充字段,把實體類加入 ObjectContext 派生類維護的 EntityCollection,然后調(diào)用 SaveChanges()寫入新紀錄:
Customer cust = new Customer()
{
CustomerID = "LAWN",
CompanyName = "Lawn Wranglers",
ContactName = "Mr. Abe Henry",
ContactTitle = "Owner",
Address = "1017 Maple Leaf Way",
City = "Ft. Worth",
Region = "TX",
PostalCode = "76104",
Country = "USA",
Phone = "(800) MOW-LAWN",
Fax = "(800) MOW-LAWO"
};
NorthwindEntities db = new NorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
可以多次 AddObject()后調(diào)用一次 SaveChanges() 全部寫入數(shù)據(jù)庫。
1. 創(chuàng)建部分加載的實體類
在之前的示例里,我們調(diào)用了 Customer 實體類的默認構(gòu)造函數(shù),它創(chuàng)建的實例沒有加載任何數(shù)據(jù)。不過,我們還可以通過在構(gòu)造函數(shù)里指定必須字段的值來減少數(shù)據(jù)庫錯誤的風(fēng)險。
每個實體類都有一個名為 CreateT 的工廠方法,例如,Customer 的實體類的工廠方法是 CreateCustomer 。看下面示例:
Customer cust = Customer.CreateCustomer("LAWN", "Lawn Wranglers");
cust.ContactName = "Mr. Abe Henry";
cust.ContactTitle = "Owner";
cust.Address = "1017 Maple Leaf Way";
cust.City = "Ft. Worth";
cust.Region = "TX";
cust.PostalCode = "76104";
cust.Country = "USA";
cust.Phone = "(800) MOW-LAWN";
cust.Fax = "(800) MOW-LAWO";
NorthwindEntities db = new NorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
我們傾向于使用默認構(gòu)造函數(shù),因為可以在一條語句里指定屬性的值,但是如果你經(jīng)常會忘記給必需的字段賦值,那么工廠方法對你就非常有用。
2. 插入關(guān)聯(lián)的實體
可以用實體類的導(dǎo)航屬性創(chuàng)建一組關(guān)聯(lián)的對象,然后一次把它們存儲到數(shù)據(jù)庫:
Customer cust = new Customer
{
CustomerID = "LAWN",
CompanyName = "Lawn Wranglers",
ContactName = "Mr. Abe Henry",
ContactTitle = "Owner",
Address = "1017 Maple Leaf Way",
City = "Ft. Worth",
Region = "TX",
PostalCode = "76104",
Country = "USA",
Phone = "(800) MOW-LAWN",
Fax = "(800) MOW-LAWO",
Orders = {
new Order{
CustomerID = "LAWN",
EmployeeID = 4,
OrderDate = DateTime.Now,
RequiredDate = DateTime.Now.AddDays(7),
ShipVia = 3,
Freight = new Decimal(24.66),
ShipName = "Lawn Wranglers",
ShipAddress = "1017 Maple Leaf Way",
ShipCity = "Ft. Worth",
ShipRegion = "TX",
ShipPostalCode = "76104",
ShipCountry = "USA"
}
}
};
NorthwindEntities db = new NorthwindEntities();
db.Customers.AddObject(cust);
db.SaveChanges();
如果單獨創(chuàng)建 Order 和 Customer 對象,就不得不顯式的添加 Order:
Customer cust = new Customer
{
CustomerID = "LAWN",
CompanyName = "Lawn Wranglers",
ContactName = "Mr. Abe Henry",
ContactTitle = "Owner",
Address = "1017 Maple Leaf Way",
City = "Ft. Worth",
Region = "TX",
PostalCode = "76104",
Country = "USA",
Phone = "(800) MOW-LAWN",
Fax = "(800) MOW-LAWO",
};
Order ord = new Order
{
CustomerID = "LAWN",
EmployeeID = 4,
OrderDate = DateTime.Now,
RequiredDate = DateTime.Now.AddDays(7),
ShipVia = 3,
Freight = new Decimal(24.66),
ShipName = "Lawn Wranglers",
ShipAddress = "1017 Maple Leaf Way",
ShipCity = "Ft. Worth",
ShipRegion = "TX",
ShipPostalCode = "76104",
ShipCountry = "USA"
};
NorthwindEntities db = new NorthwindEntities();
db.Customers.AddObject(cust);
db.Orders.AddObject(ord);
db.SaveChanges();
更新實體類和修改對象的屬性一樣簡單:
NorthwindEntities db = new NorthwindEntities();
Customer cust = (from c in db.Customers
where c.CustomerID == "LAWN"
select c).Single();
cust.ContactName = "John Smith";
cust.Fax = "(800) 123 1234";
db.SaveChanges();
Single():返回序列的唯一元素;如果該序列并非恰好包含一個元素,則會引發(fā)異常。
刪除也很簡單:
NorthwindEntities db = new NorthwindEntities();
IEnumerable<Order_Detail> ods = from o in db.Order_Details
where o.OrderID == 10248
select o;
// 對 LINQ 查詢而返回的結(jié)果集進行處理
// 要么使用 Single() 取出單條記錄
// 要么就迭代集合進行處理
foreach (Order_Detail o in ods)
{
db.Order_Details.DeleteObject(o);
}
db.SaveChanges();
注意:Entity Framework 不會刪除關(guān)聯(lián)的實體對象,因此調(diào)用 SaveChanges()前必須小心地刪除所有通過外鍵約束關(guān)聯(lián)的對象。
Entity Framework 默認使用樂觀并發(fā)模型,也就是說在讀取數(shù)據(jù)后,它不檢查是否有人修改了數(shù)據(jù)庫中的數(shù)據(jù)。調(diào)用 SaveChanges()時,所有待更新數(shù)據(jù)全部被寫到數(shù)據(jù)庫中,即使他人已經(jīng)更新了有沖突的記錄時也是如此。
樂觀并發(fā)會導(dǎo)致痛苦的數(shù)據(jù)一致性問題。
可以讓 Entity Framework 在更新前檢查數(shù)據(jù)庫是否由第三方執(zhí)行了更改,雖然這還是樂觀并發(fā),因為實體對象不會鎖住數(shù)據(jù)庫的任何對象。但至少它可以在發(fā)生問題時給你提醒。
打開實體數(shù)據(jù)模型,選中字段,在屬性中設(shè)置“并發(fā)模式”為“Fixed”,如下圖:
為實體對象啟用并發(fā)沖突檢查后,試圖更新已經(jīng)被更新過的數(shù)據(jù)時,會得到一個 OptimisticConcurrencyException 。為了模擬并發(fā)異常,我們使用 Entity Framework 執(zhí)行更新,然后通過 ExecuteStatementInDb 方法直接執(zhí)行會造成沖突的 SQL 語句:
protected void Page_Load(object sender, EventArgs e)
{
NorthwindEntities db = new NorthwindEntities();
Customer cust = db.Customers
.Where(c => c.CustomerID == "LAZYK")
.Select(c => c).First();
Response.Write(string.Format("Initial value {0}<br />", cust.ContactName));
// change the record outside of the entity framework
string sql = string.Format(@"update Customers set ContactName = 'Samuel Arthur Sanders'
where CustomerID = 'LAZYK'");
ExecuteStatementInDb(sql);
// modify the customer
cust.ContactName = "John Doe";
// save the changes
try
{
db.SaveChanges();
}
catch (OptimisticConcurrencyException)
{
Response.Write("Detected concurrency conflict - giving up<br />");
}
finally
{
sql = string.Format(@"select ContactName from Customers
where CustomerID = 'LAZYK'");
string dbValue = GetStringFromDb(sql);
Response.Write(string.Format("Database value: {0}<br />", dbValue));
Response.Write(string.Format("Cached value: {0}<br />", cust.ContactName));
}
}
private void ExecuteStatementInDb(string sql)
{
string conStr = WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
SqlConnection conn = new SqlConnection(conStr);
SqlCommand cmd = new SqlCommand(sql, conn);
try
{
conn.Open();
Response.Write("Executeing Sql statement against database with ADO.NET ...<br />");
cmd.ExecuteNonQuery();
Response.Write("Database updated.<br />");
conn.Close();
}
catch (Exception err)
{
throw new ApplicationException(err.Message);
}
finally
{
conn.Close();
}
}
private string GetStringFromDb(string sql)
{
string conStr = WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
SqlConnection conn = new SqlConnection(conStr);
SqlCommand cmd = new SqlCommand(sql, conn);
try
{
conn.Open();
object obj = cmd.ExecuteScalar();
conn.Close();
return obj.ToString();
}
catch (Exception err)
{
throw new ApplicationException(err.Message);
}
finally
{
conn.Close();
}
}
因為在 ContactName 字段上做了并發(fā)檢查,因此在 Entity Framework 更新時捕獲了這個異常,最終更新并沒有成功。
不過,我們除了要檢查數(shù)據(jù)的差異,還是想把數(shù)據(jù)寫回數(shù)據(jù)庫,這時可以調(diào)用 ObjectContext.Refresh()來解決這一問題,可以在捕獲異常時增加這樣一條代碼:
catch (OptimisticConcurrencyException)
{
Response.Write("Detected concurrency conflict - giving up<br />");
db.Refresh(RefreshMode.StoreWins, cust);
}
Refresh():
RefreshMode.StoreWins 表示用數(shù)據(jù)庫中的值更新實體對象的值
RefreshMode.ClientWins 表示用實體對象的值更新數(shù)據(jù)庫中的值
讓我們回顧一下這里所發(fā)生的一切。我們試圖向數(shù)據(jù)庫寫入在其他某處已經(jīng)被更新了的數(shù)據(jù)。Entity Framework 檢測到了并發(fā)沖突并拋出 OptimisticConcurrencyException 異常,讓我們知道發(fā)生了問題。我們用數(shù)據(jù)庫里的數(shù)據(jù)刷新修改了實體對象,它使得我們重新回到一致的狀態(tài)。
但我們的更新發(fā)生了什么?嗯,什么也沒有發(fā)生。如果我們一定要應(yīng)用自己的修改的話,就應(yīng)該使用 RefreshMode.ClientWins 枚舉值,并再次調(diào)用 SaveChanges():
catch (OptimisticConcurrencyException)
{
Response.Write("Detected concurrency conflict - giving up<br />");
db.Refresh(RefreshMode.ClientWins, cust);
db.SaveChanges();
}
這一回,就好像說“我知道有并發(fā)沖突發(fā)生了,但我也堅持我的更新”那樣。為妥善的處理并發(fā)沖突時我們要指出一點:刷新實體對象時可能會有人再次修改數(shù)據(jù),也就是說第二次調(diào)用 SaveChanges()可能會引發(fā)另一個 OptimisticConcurrencyException 異常,為了解決這個問題,我們可以循環(huán)嘗試應(yīng)用更新:
// modify the customer
cust.ContactName = "John Doe";
int maxAttempts = 5;
bool recordsUpdated = false;
for (int i = 0; i < maxAttempts && !recordsUpdated; i++)
{
try
{
db.SaveChanges();
recordsUpdated = true;
}
catch (Exception)
{
db.Refresh(RefreshMode.ClientWins, cust);
}
}
聯(lián)系客服