NHibernate 使用 XML 設定對應,Castle ActiveRecord(底層仍用 NHibernate)採用 Attribute 方式。一個非侵入式、一個侵入式。有人偏好 XML 彈性免重新編譯,有人偏好 Attribute 可維護性佳且具編譯期檢查。
在 LINQ to SQL 中,這兩種方式皆支援,且走得更遠,提供四種對應設定途徑:
對應 Attribute 位於 System.Data.Linq.Mapping 命名空間。常用 Attribute:Table、Column、Association,另有 Database、Provider、Function 等。
套用於類別,不可重複、不可繼承。定義類別與資料表對應。若類別未標註 Table,即使屬性標註 Column 也無效,執行時會拋出 InvalidOperationException。唯一屬性 Name 用於類別名與資料表名不同時指定表名。
屬性豐富,命名直觀易懂。
列舉型別:Default、Always、Never、OnInsert、OnUpdate。控制執行 Insert/Update 後,屬性如何與資料庫被修改欄位同步。
Default:預設,若欄位有預設值且 IsDbGenerated=true 則同步。Always:總是同步。Never:從不同步。OnInsert:插入後同步(如自增主鍵)。OnUpdate:更新時同步。實測:預設情況下插入後會自動查回 IsDbGenerated=true 的欄位(如身分識別欄位)。若將 Title 設為 AutoSync.Always,插入後也會查回 Title。
指定對應資料庫欄位是否允許 NULL。若不允許而指派 null 會拋出例外。注意:NULL 不等於空字串或零。
若使用 CreateDatabase 從對應類別建立資料表,建議明確指定,避免 LINQ to SQL 從屬性型別推斷導致不精確。例:[Column(DbType="NVarchar(50) not null")] public string Title。這也意味著可先設計領域物件再建立資料庫(真正的 ORM)。
表示計算欄位。例:資料庫存美元價格,取出時自動換算人民幣:[Column(Expression="price * 6")] public float Price。注意:匯率波動時硬編碼不可取,僅示範用法。
標示屬性值由資料庫產生(如自增欄位、有預設值的欄位),程式碼無需指派。
標示主鍵。複合主鍵可在多個屬性上標註。
樂觀並行控制相關,後續詳述。
屬性名與欄位名不一致時指定欄位名。
當屬性 setter 包含複雜邏輯,但從資料庫取值時不想執行 setter,可指定私有欄位名稱:
private float _price;
[Column(Storage="_price")]
public float Price {
get { return _price; }
set { _price = value * 20; } // 複雜邏輯
}
建立實體間關聯。範例:
[Association(ThisKey="Id", OtherKey="BlogId")]
public EntitySet<Post> Posts { get; set; }
核心屬性:ThisKey(本端關聯鍵,預設為 IsPrimaryKey 屬性)、OtherKey(關聯端鍵,預設為關聯類別的識別欄位)。繼承自 DataAttribute,也有 Name(建立資料庫關聯時用)與 Storage。
DataContext 有多載建構子:DataContext(string, MappingSource)。MappingSource 為抽象類別,兩個實作:AttributeMappingSource(預設)與 XmlMappingSource。
使用 XML 對應:
XmlMappingSource mapping = XmlMappingSource.FromXml(File.ReadAllText(@"e:\cnblogs\map.xml"));
DataContext dbContext = new DataContext(connectionString, mapping);
XML 格式範例:
<?xml version="1.0" encoding="utf-16"?>
<Database Name="Cnblogs" xmlns="https://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="posts">
<Type Name="Yuyijq.Linq.Cnblogs.Domain.Post">
<Column Name="postid" Member="Id" IsPrimaryKey="true" IsDbGenerated="true" />
<Column Name="blogid" Member="BlogId" />
<Column Name="title" Member="Title" />
<Column Name="body" Member="Body" />
<Column Name="createdate" Member="CreateDate" IsDbGenerated="true"/>
</Type>
</Table>
</Database>
結構:Database → Table → Type → Column/Association。與 Attribute 方式對應,多一個 Member 指定實體屬性。
可整合 MSBuild 實現自動化。支援 /serialization: 開關(None 或 Unidirectional),產生可序列化實體類別(適合 WCF/Web Service 傳遞)。詳細用法輸入 SqlMetal /? 查詢。
專案中新增「LINQ to SQL 類別」項目,從伺服器總管拖曳資料表至設計面,工具列建立關聯。產生三個檔案:
.dbml:XML 格式對應檔.cs:部分類別程式碼,與 .dbml 共同編譯.layout:設計器版面資訊,不影響程式原計劃三篇完成 How Do I,內容較多拆分為四篇。下一篇將詳細介紹 DataContext、Update/Delete 時的 RowVersion、交易與延遲載入/執行等主題。