LINQ(數據庫操作增、刪、改及併發管理)

轉自:http://www.cnblogs.com/SkySoot/archive/2012/08/23/2652216.html

插入

       爲了在數據庫裏創建新紀錄,需要創建相應實體類的新實例,填充字段,把實體類加入 ObjectContext 派生類維護的 EntityCollection,然後調用 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()後調用一次 SaveChanges() 全部寫入數據庫。

 

1. 創建部分加載的實體類

       在之前的示例裏,我們調用了 Customer 實體類的默認構造函數,它創建的實例沒有加載任何數據。不過,我們還可以通過在構造函數裏指定必須字段的值來減少數據庫錯誤的風險。

       每個實體類都有一個名爲 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();

       我們傾向於使用默認構造函數,因爲可以在一條語句裏指定屬性的值,但是如果你經常會忘記給必需的字段賦值,那麼工廠方法對你就非常有用

 

2. 插入關聯的實體

       可以用實體類的導航屬性創建一組關聯的對象,然後一次把它們存儲到數據庫:

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();

       如果單獨創建 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():返回序列的唯一元素;如果該序列並非恰好包含一個元素,則會引發異常。

 

刪除

       刪除也很簡單:

NorthwindEntities db = new NorthwindEntities();
 
IEnumerable<Order_Detail> ods = from o in db.Order_Details
                                where o.OrderID == 10248
                                select o;
 
// 對 LINQ 查詢而返回的結果集進行處理
// 要麼使用 Single() 取出單條記錄
// 要麼就迭代集合進行處理
foreach (Order_Detail o in ods)
{
    db.Order_Details.DeleteObject(o);
}
 
db.SaveChanges();

注意:Entity Framework 不會刪除關聯的實體對象,因此調用 SaveChanges()前必須小心地刪除所有通過外鍵約束關聯的對象

 

管理併發

       Entity Framework 默認使用樂觀併發模型,也就是說在讀取數據後,它不檢查是否有人修改了數據庫中的數據。調用 SaveChanges()時,所有待更新數據全部被寫到數據庫中,即使他人已經更新了有衝突的記錄時也是如此。

       樂觀併發會導致痛苦的數據一致性問題

       可以讓 Entity Framework 在更新前檢查數據庫是否由第三方執行了更改,雖然這還是樂觀併發,因爲實體對象不會鎖住數據庫的任何對象。但至少它可以在發生問題時給你提醒。

       打開實體數據模型,選中字段,在屬性中設置“併發模式”爲“Fixed”,如下圖:

image

 

處理併發衝突

       爲實體對象啓用併發衝突檢查後,試圖更新已經被更新過的數據時,會得到一個 OptimisticConcurrencyException 。爲了模擬併發異常,我們使用 Entity Framework 執行更新,然後通過 ExecuteStatementInDb 方法直接執行會造成衝突的 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();
    }
}

image

       因爲在 ContactName 字段上做了併發檢查,因此在 Entity Framework 更新時捕獲了這個異常,最終更新並沒有成功。

       不過,我們除了要檢查數據的差異,還是想把數據寫回數據庫,這時可以調用 ObjectContext.Refresh()來解決這一問題,可以在捕獲異常時增加這樣一條代碼:

catch (OptimisticConcurrencyException)
{
    Response.Write("Detected concurrency conflict - giving up<br />");
    db.Refresh(RefreshMode.StoreWins, cust);
}

Refresh():

  • 參數一:RefreshMode 枚舉
  • 參數二:要刷新的對象

RefreshMode.StoreWins 表示用數據庫中的值更新實體對象的值

RefreshMode.ClientWins 表示用實體對象的值更新數據庫中的值

 

       讓我們回顧一下這裏所發生的一切。我們試圖向數據庫寫入在其他某處已經被更新了的數據。Entity Framework 檢測到了併發衝突並拋出 OptimisticConcurrencyException 異常,讓我們知道發生了問題。我們用數據庫裏的數據刷新修改了實體對象,它使得我們重新回到一致的狀態。

       但我們的更新發生了什麼?嗯,什麼也沒有發生。如果我們一定要應用自己的修改的話,就應該使用 RefreshMode.ClientWins 枚舉值,並再次調用 SaveChanges():

catch (OptimisticConcurrencyException)
{
    Response.Write("Detected concurrency conflict - giving up<br />");
    db.Refresh(RefreshMode.ClientWins, cust);
    db.SaveChanges();
}

       這一回,就好像說“我知道有併發衝突發生了,但我也堅持我的更新”那樣。爲妥善的處理併發衝突時我們要指出一點:刷新實體對象時可能會有人再次修改數據,也就是說第二次調用 SaveChanges()可能會引發另一個 OptimisticConcurrencyException 異常,爲了解決這個問題,我們可以循環嘗試應用更新:

// 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);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章