程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Linq to SQL對象的標識

Linq to SQL對象的標識

編輯:關於.NET

很多朋友都向我提過,希望我寫一下關於Linq to SQL 或者 VS 插件方面的文章。盡管市面上有很多 Linq to SQL 的書籍,但是都是介紹怎麼用,缺乏深度。關於 VS 插件方面的書籍也是很顯淺,按書籍做出來的東西,只能是學生級別的東西,根本拿不出手。他們覺得我有這個能力寫好。

從技術能力的角度來說,的確是不存在什麼問題,但是,要把一門技術講精講透,是花很時間的事情。自己付出了很多,如果不能得到讀者的認同,那這個專題寫下去也沒什麼意義了。這個專題不是教你怎麼使用Linq to SQL,而是讓你明白Linq to SQL的原理,對於想寫ORM的朋友,絕對不可錯過。寫完《深入了解 Linq to SQL》這個系列後,下一個系列就是《VS 插件開發了》,所以,大家如果希望我繼續寫下去,請記得點推薦。你們的推薦,就是我寫下去的動力。

概述

關於對象的標識,簡單點說,就是主鍵相同的對象,在數據上下文的緩存中,只有一個。數據上下文,在加載數據,創建對象之後,接著對所創建的每個實體類的對象,都會克隆一份對象副本(淺復制)記住這點,我們在後面要用到,用來保存對象的初始值,當對象的屬性值修改後,副本的屬性值是不改的。注意,只有實體類對象才會創建對象副本,而匿名類對象是不會生成副本,也只實體。我們看下面一段代碼:

例一

var c1 = db.Categories.Single(o => o.CategoryId == 1);
var c2 = db.Categories.Single(o => o.CategoryId == 1);

在例一這個例子中,c1、c2 是相等的,我們再來看下面一個例子:

例二

var c1 = db.Categories.Select(o => new { o.CategoryId, o.CategoryName }).Single(o => o.CategoryId == 1);
var c2 = db.Categories.Select(o => new { o.CategoryId, o.CategoryName }).Single(o => o.CategoryId == 1);

這個例二這個例子中,c1、c2 是不相等的。

為什麼匿名對象不支持對象標識呢?因為匿名對象,不一定有主鍵。而 Linq to SQL ,是通過實體的主鍵來標識一個實體對象的,當從數據庫加載完數據,會先根據主鍵檢索緩存中是否有已經存在的對象,沒有才會創建對象。這樣會引起一個什麼樣的問題呢?執行下面的代碼。

var c1 = db.Categories.Single(o => o.CategoryId == 1);

假設 CategoryId 為 1 的 CategoryName 為 “Beverages”,打開數據庫,把該值改為“New Name”後,如下圖:

圖一

然後再執行下面的代碼:

var c2 = db.Categories.Single(o => o.CategoryId == 1);

這時候 c2 修改後的 c2.CategoryName 的值是什麼呢?仍然為“Beverages”!因為第二次加載完數據的時候,由於已經存在了主鍵(CategoryID)為“1”的Category,所以在第二次的加載中,不再創建新的對象,以是使用之前所創建的Category對象。那怎麼樣才能使用 c2 的 CategoryName 的值為最新值“New Name”呢?調用數據上下文的 Refresh 方法即可。

 db.Refresh(RefreshMode.OverwriteCurrentValues, c2);

跟蹤屬性的更改

我們先來看一個更新的例子:

var c1 = db.Categories.Single(o => o.CategoryId == 1);
c1.CategoryName = "xxx";
db.SubmitChanges();
    
c1.CategoryName = "xxx";
db.SubmitChanges();

通過查看生成的SQL,我們可以發現,盡管 SubmitChagens 方法執行了兩次,但是實際上SQL只執行了一次,因為第二次的 CategoryName 並沒有修改,那麼Linq to SQL如何得知某一個屬性是修改了或者沒有呢?我們看Category實體類的定義,為了節省篇幅,只選取一部份。

[Table(Name="Categories")]
public partial class Category : INotifyPropertyChanging, INotifyPropertyChanged
{
    [Column(Storage="_CategoryName", DbType="VarChar(15)", CanBeNull=false, UpdateCheck=UpdateCheck.Never)]
    public string CategoryName
    {
        get
        {
            return this._CategoryName;
        }
        set
        {
            if ((this._CategoryName != value))
            {
                this.OnCategoryNameChanging(value);
                this.SendPropertyChanging();
                this._CategoryName = value;
                this.SendPropertyChanged("CategoryName");
                this.OnCategoryNameChanged();
            }
        }
    }
            
            
    protected virtual void SendPropertyChanged(String propertyName)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我們可以看得到,實體類Category實現了 INotifyPropertyChanged 的接口,通過這個接口,就可以得知某個屬性是否已經更改了,但是,事實上Category實體類不實現上面的接口,也是沒有問題的,如下所示:

[Table(Name = "Categories")]
public partial class Category
{
    [Column(Storage = "_CategoryName", DbType = "VarChar(15)", CanBeNull = false, UpdateCheck = UpdateCheck.Never)]
    public string CategoryName
    {
        get { return this._CategoryName; }
        set { this._CategoryName = value; }
    }
}

為什麼這樣也可以呢?記得我們剛才說到對象副本嗎?當 Category 沒有實現 INotifyPropertyChanged 這個接口時,Linq to SQL會將Category實體對象和原始的對象副本作比較來得知Category的屬性值是否更改了。

但是下面的定義,會導致 CategoryName 的值即使用修改了,也不會更新到數據庫。因為,當 CategoryName 的值發生變化的,並沒有通知到接口 INotifyPropertyChanged,而當實體類繼承於INotifyPropertyChanged時,Linq to SQL是依賴INotifyPropertyChanged來獲取值被更改的屬性。

[Table(Name = "Categories")]
public partial class Category: INotifyPropertyChanged
{
    [Column(Storage = "_CategoryName", DbType = "VarChar(15)", CanBeNull = false, UpdateCheck = UpdateCheck.Never)]
    public string CategoryName
    {
        get { return this._CategoryName; }
        set { this._CategoryName = value; }
    }
}

由上面我們可以知道,通過對象標識,可以使得Model的定義最為簡潔,在Linq to SQL的設計裡,你會常常可以看到,很多地方都遵循簡潔模型這個原則,這個原則,在後面的內容裡會給大家介紹。同時你會發現,如果對於沒有定義好主鍵的實體,是不能進行添加、更新、刪除的操作,因為這些操作都依賴於對象標識,而對象標識又需要實體的主鍵。又因為主鍵是用來標識對象的,所以主鍵是不能何改的。

本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved