ADO.NET
MSDN:ADO.NET 概觀

使用 DataTable 物件
DataTable 物件
DataTable 是一種表格式的資料表現概念,它由資料列(rows)、資料欄(columns)、條件約束(constraints)組成。
常用方法:
- AcceptChanges :認可自前一次呼叫 AcceptChanges 以來對這個資料表所做的所有變更。
- GetChanges :取得自前一次呼叫 AcceptChanges 以來所做的所有變更。
- Load :載入資料。( 視 LoadOption 參數,執行不同的載入操作)
- LoadDataRow :尋找並更新特定資料列。 如果找不到符合的資料列,則使用指定值來建立新的資料列。
- Merge :將指定的 DataTable 與目前的 DataTable 合併。
- NewRow :使用與資料表相同的結構描述來建立新的 DataRow。
- OnRowChanged :引發 RowChanged 事件。
- Select :取得符合篩選條件的所有 DataRow 物件的陣列。
- Copy :複製這個 DataTable 的結構和資料。
建立 DataTable
private DataTable CreateDataTable()
{
//=====================================================
//Create the DataTable named "Employee"
//=====================================================
DataTable Employee = new DataTable("Employee");
//=====================================================
//Add the DataColumn
//=====================================================
DataColumn col1 = new DataColumn("EmpNo");
col1.DataType = typeof(string);
col1.MaxLength = 10;
col1.Unique = true; // Unique Key
col1.AllowDBNull = false;
col1.DefaultValue = ""; // DefaultValue
col1.Caption = "EmpNo";
col1.AutoIncrement = false;
DataColumn col2 = new DataColumn("FirstName");
col2.MaxLength = 20;
col2.AllowDBNull = false;
DataColumn col3 = new DataColumn("LastName");
col3.MaxLength = 20;
col3.AllowDBNull = false;
DataColumn col4 = new DataColumn("Age");
col3.DataType = typeof(Int16);
col1.DefaultValue = 0;
// Add [ Expression columns ]
DataColumn col5 = new DataColumn("LastName and FirstName");
col5.DataType = typeof(string);
col5.MaxLength = 40;
col5.Expression = "LastName + ',' + FirstName";
col1.AllowDBNull = false;
Employee.Columns.Add(col1);
Employee.Columns.Add(col2);
Employee.Columns.Add(col3);
Employee.Columns.Add(col4);
Employee.Columns.Add(col5);
//=====================================================
// Set Primary Key Columns
//=====================================================
Employee.PrimaryKey = new DataColumn[] { col1 };
return Employee;
}
Copy 、 Clone 、 Import
- Copy :複製完整 DataTable 的結構和資料。
- Clone :僅複製 (Clone) DataTable 的結構,包括所有 DataTable 結構描述和條件約束。。
- ImportRow :將 DataRow 複製到 DataTable,並且保留該資料列的 Current 和 Original 版本。
DataTable employee = CreateDataTable(); // Copy DataTable DataTable copy = employee.Copy(); // Clone DataTable DataTable clone = employee.Clone(); // Importing datarows to datatable clone.ImportRow(employee.Rows[0]);
使用 ImportRow 時須注意:
- 呼叫 NewRow 會使用現有資料表結構描述,將資料列加入至資料表,但具有資料列的預設值,並將 DataRowState 設定為 Detached。
- 呼叫 ImportRow 會保留現有的 DataRowState,以及資料列中的值。
- 新資料行會加入至資料表的結尾。
- 如果匯入的資料行違反條件約束 (如:PK),會引發 Exception,不會被新增到資料表中。
DataColumn 物件
DataColumn 是用來表示 DataTable 中資料行的結構描述。
底下是 DataColumn 預設的屬性值:
- DataType :預設值為 String
- MaxLength :預設值為 -1 (表示不執行最大長度的檢查)
- Unique :預設值為 False
- AllowDBNull :預設值為 True
- Caption :預設值為 ColumnName 的值。
- AutoIncrement :資料行是否自動遞增資料行的值。預設值為 false。
- Expression :設定運算式的欄位。
建立 DataTable 的 PK 欄位
Employee.PrimaryKey = new DataColumn[] { col1 };
篩選 DataTable
使用 DataTable.Select()
DataTable.Select 可以用來取得符合篩選條件的所有 DataRow 物件的陣列。
protected void btnDataTableSelect_Click(object sender, EventArgs e)
{
// load test data
DataTable dataTable = TestData.CreateEmployeeDataTable();
TestData.SetEmployeeData(dataTable);
// set filter expressiong
string expression = "FirstName = 'AAA'";
DataRow[] foundRows = dataTable.Select(expression);
// import filter result to new datatable
DataTable tableFiltered = dataTable.Clone(); // 複製datatable結構描述。
foreach (DataRow row in foundRows)
{
tableFiltered.ImportRow(row);
}
gv1.DataSource = tableFiltered;
gv1.DataBind();
}
使用 DataView.RowFilter()
RowFilter 是 DataView 物件的屬性,用來設定該 DataView 的篩選條件。 你可以透過 RowFilter 屬性的設定,取得符合篩選條件的 DataView 物件。
protected void btnDataViewRowFilter_Click(object sender, EventArgs e)
{
// load test data
DataTable dtEmpData = TestData.CreateEmployeeDataTable();
TestData.SetEmployeeData(dtEmpData);
// Use the RowFilter method of DataView object to find all rows matching the filter.
dtEmpData.DefaultView.RowFilter = "FirstName = 'AAA'";
DataTable dtFiltered = dtEmpData.DefaultView.ToTable();
// 使用篩選後的 datatable 當做資料來源
gv1.DataSource = dtFiltered;
gv1.DataBind();
// DataView 物件也可以直接當做資料來源
dtEmpData.DefaultView.RowFilter = "FirstName = 'AAA'";
gv1.DataSource = dtEmpData.DefaultView;
gv1.DataBind();
}
使用 DataRow 物件
DataRow 是用來表示 DataTable 的欄位定義,也就因此它必須由 DataTable 產生。
DataRow 物件的常用方法
- BeginEdit :在 DataRow 物件上開始批次更改。
- EndEdit :結束批次更改。
- CancelEdit :取消批次更改。
- AcceptChanges :認可自前一次呼叫 AcceptChanges 以來,對這個資料列所做的所有變更。
- RejectChanges :拒絕自前一次呼叫 AcceptChanges 以來,對資料列所做的所有變更。
- Delete :刪除 DataRow。
- HasVersion :判斷是否有指定的版本。
當
將資料加入到 DataTable 中
要將資料加入到 DataTable ,可以使用 Add 、 Load 或 LoadDataRow 方法。
Add :將資料逐筆加入到 DataTable 中
- 透過 DataTable.NewRow 以建立符合該 Table 結構的 DataRow 物件。
- 再將 DataRow 加入到 DataTable.Rows 集合之中。
public void Add(DataRow row); public DataRow Add(params object[] values); //若是自動編號欄位,該欄位必須傳入 null 以取得該資料行的預設值。
// 1) 先建立 DataRow , 再加入 DataTable
DataRow newemployee = employee.NewRow();
newemployee["EmpNo"] = "123456789A";
newemployee["FirstName"] = "Nancy";
newemployee["LastName"] = "Davolio";
newemployee["Age"] = 30;
employee.Rows.Add(newemployee);
// 2) 直接在 DataTable 加入一筆資料
employee.Rows.Add("987654321X", "Andrew", "Fuller", 40);
Load :資料來源的值填滿 DataTable。
如果 DataTable 已經包含資料列,則資料來源中的資料將會與現有的資料列合併。 合併的原則,則視 LoadOption 參數決定。
使用 Load 或 LoadDataRow 方法時,可以指定是否更新現有 DataTable 中的資料,所以必須指定 PK 欄位。 當載入的資料與現有的資料具有相同的 PK 時,它就會根據 LoadOption 參數值做不同的處理。
public void Load(IDataReader reader); public void Load(IDataReader reader, LoadOption loadOption); public virtual void Load(IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler);
DataTable NewEmployees = CreateDataTable();
NewEmployees.Columns.Remove("LastName and FirstName");
NewEmployees.Rows.Add("1234567890", "AAA", "BBB", 10);
NewEmployees.Rows.Add("1234567891", "CCC", "EEE", 20);
DataTableReader reader = new DataTableReader(NewEmployees);
Employee.Load(reader);
LoadDataRow :尋找並更新特定資料列
如果找不到符合的資料列,則會將該資料列加入到 DataTable 。
public DataRow LoadDataRow(object[] values, bool fAcceptChanges); public DataRow LoadDataRow(object[] values, LoadOption loadOption);
employee.LoadDataRow(
new object[] { "1234567891", "CCC", "DDD", 30 },
LoadOption.OverwriteChanges); //更新 Current , 且更新 Original
LoadOption
這個列舉值是指明若載入的資料與現有資料 PK 重複時,應如何套用到現有的資料列上。
public enum LoadOption
{
OverwriteChanges = 1, // OverwriteRow, Update Original and Current Values
PreserveChanges = 2, // PreserveCurrentValues (default), Update Original Values
Upsert = 3, // UpdateCurrentValues
}
| LoadOption | Version Value Changed | RowState |
|---|---|---|
| OverwriteChanges | 以收到的資料列值,更新 Original and Current 。 | 無論原狀態為何,只要有被更新的列, 其 RowState 屬性會設定為 Unchanged 。 新增的列, RowState 屬性會設定為 Unchanged 。 |
| PreserveChanges | 以收到的資料列值,更新 Original 。 | 被更新的列, 若其 RowState 原本為 Added ,則會設定為 Modified 。 若其 RowState 原本為 Modified ,則會設定為 Modified 。 若其 RowState 原本為 Deleted ,則會保留為 Deleted 。 新增的列, RowState 屬性會設定為 Unchanged 。 |
| Upsert | 以收到的資料列值,更新 Current 。 | 被更新的列, 若其 RowState 原本為 Added ,則會保留為 Added 。 若其 RowState 原本為 Modified ,則會保留為 Modified 。 若其 RowState 原本為 Deleted ,則會設定為 Modified 。 新增的列, RowState 屬性會設定為 Added 。 |
範例:
//載入測試資料:Modify , Unchanged 和 Added 各二筆
DataTable dTable = CreateDataTable();
dTable.Rows.Add("001", "AAA", "aaa", 10); //Modified
dTable.Rows.Add("002", "BBB", "bbb", 20); //Unchanged
dTable.AcceptChanges();
dTable.Rows.Add("003", "CCC", "ccc", 30); //Added
dTable.Rows.Add("004", "DDD", "ddd", 40); //Added
dTable.Rows[0][3] = 15;
dTable.Rows[2][3] = 35;
dTable.LoadDataRow(new object[] { "001", "AAA", "aaa", 11 }, LoadOption.PreserveChanges);
dTable.LoadDataRow(new object[] { "003", "CCC", "ccc", 31 }, LoadOption.PreserveChanges);
dTable.LoadDataRow(new object[] { "005", "EEE", "eee", 51 }, LoadOption.PreserveChanges);
dTable.LoadDataRow(new object[] { "001", "AAA", "aaa", 11 }, LoadOption.OverwriteChanges);
dTable.LoadDataRow(new object[] { "003", "CCC", "ccc", 31 }, LoadOption.OverwriteChanges);
dTable.LoadDataRow(new object[] { "005", "EEE", "eee", 51 }, LoadOption.OverwriteChanges);
dTable.LoadDataRow(new object[] { "001", "AAA", "aaa", 11 }, LoadOption.Upsert);
dTable.LoadDataRow(new object[] { "003", "CCC", "ccc", 31 }, LoadOption.Upsert);
dTable.LoadDataRow(new object[] { "005", "EEE", "eee", 51 }, LoadOption.Upsert);



DataRowState
DataRowState 這個列舉值是用來取得 DataRow 物件的狀態。
public enum DataRowState
{
Detached = 1, // DataRow is created but not added to a DataTable.
Unchanged = 2, // DataRow has not changed since the last call to the AcceptChanges method.
Added = 4, // DataRow is added to a DataTable.
Deleted = 8, // DataRow is deleted using the Delete method of the DataRow.
Modified = 16, // DataRow has been modified since the last time the AcceptChanges method was called.
}

DataRowVersion
DataRowVersion 這個列舉值是用來描述 DataRow 的版本。
public enum DataRowVersion
{
Original = 256, // 資料列含有原始值(original value)。
Current = 512, // 資料列含有目前的值(current value)。
Proposed = 1024, // 資料列含有建議值(proposed value)。
Default = 1536, // 當 RowState 為 Added、Modified 時,其預設的版本 Current ; 當 DataRowState 為 Detached 時,版本則為 Proposed。
}
DataRow 的版本會隨下列狀況下變更:
- 當 DataRow 建立時,只會有單一版本,叫做 Current 。
- 當叫用 BeginEdit 時,變更的資料會放在第二個版本,叫做 Proposed 。 所以會有 Proposed 和 Current 。
- 當叫用 EndEdit 時, Current 變成 Original , Proposed 變成 Current 。 所以會有 Original 和 Current 。
- 當再次叫用 BeginEdit 時,會複製 Current 到 Proposed 。 所以會有 Original 和 Current 和 Proposed 。
- 當叫用 EndEdit 時, Proposed 變成 Current 。 所以會有 Original 和 Current 。
- 當叫用 AcceptChanges 時, Original 值會變成和 Current 值相同。

取得 DataRow 不同的版本。
if (dRow.HasVersion(DataRowVersion.Original))
myDebug.Write( dRow["Age", DataRowVersion.Original].ToString() );
if (dRow.HasVersion(DataRowVersion.Proposed))
myDebug.Write( dRow["Age", DataRowVersion.Proposed].ToString() );
DataRow 的預設版本:
- 當 RowState 為 Added 、 Modified 時,若讀取資料值時,會取得 Current 版本的值。
- 當 RowState 為 Deleted 時,若讀取資料值時,會引發 Exception 。
- 當叫用 BeginEdit 方法後,若讀取資料值時,會取得 Proposed 版本的值。
範例1
DataTable dTable = CreateDataTable();
DataRow dRow = dTable.NewRow();
dRow["EmpNo"] = "12345";
dRow["FirstName"] = "vito";
dRow["LastName"] = "shao";
dRow["Age"] = 10;
ShowState("1)After DataTable.NewRow", dTable, dRow);
dTable.Rows.Add(dRow);
ShowState("2)After DataTable.Rows.Add", dTable, dRow);
dRow.AcceptChanges();
ShowState("3)After Add + AcceptChanges", dTable, dRow);
dRow.BeginEdit();
dRow["Age"] = 22;
ShowState("4)After BeginEdit", dTable, dRow);
dRow.CancelEdit();
ShowState("5)After CancelEdit", dTable, dRow);
dRow.BeginEdit();
dRow["Age"] = 33;
dRow.EndEdit();
dRow.CancelEdit();
ShowState("6)After BeginEdit+EndEdit+CancelEdit", dTable, dRow);
dRow["Age"] = 44;
dTable.RejectChanges();
ShowState("9)After modify + RejectChanges", dTable, dRow);
dRow["Age"] = 55;
dRow.AcceptChanges();
ShowState("7)After modify + AcceptChanges", dTable, dRow);
dRow.Delete();
ShowState("8)After Delete", dTable, dRow);
dTable.AcceptChanges();
ShowState("9)After Delete + AcceptChanges", dTable, dRow);
// RowState RowCounts Age Default Current Original Proposed
// ========= ========== === ======= ======= ======== ========
//1)After DataTable.NewRow Detached 0 10 10
//2)After DataTable.Rows.Add Added 1 10 10 10
//3)After Add + AcceptChanges Unchanged 1 10 10 10 10
//4)After BeginEdit Unchanged 1 22 22 10 10 22 (Default=Proposed)
//5)After CancelEdit Unchanged 1 10 10 10 10
//6)After BeginEdit+EndEdit+CancelEdit Modified 1 33 33 33 10
//9)After modify + RejectChanges Unchanged 1 10 10 10 10
//7)After modify + AcceptChanges Unchanged 1 55 55 55 55
//8)After Delete Deleted 1 55
//9)After Delete + AcceptChanges Detached 0
範例2
DataTable Employees = CreateDataTable();
Employees.Rows.Add("001", "AAA", "BBB", 10);
Employees.Rows.Add("002", "CCC", "DDD", 20);
Employees.AcceptChanges();
DataRow emp = Employees.Rows[1];
DataRowState rowState;
String LastName = "";
rowState = emp.RowState; //Unchanged
emp["LastName"] = "EEE";
rowState = emp.RowState; //Modified
emp.AcceptChanges();
rowState = emp.RowState; //Unchanged
emp.BeginEdit();
emp["LastName"] = "FFF";
rowState = emp.RowState; //Unchanged
LastName = emp["LastName"].ToString();
if (emp.HasVersion(DataRowVersion.Current))
LastName = emp["LastName", DataRowVersion.Current].ToString();
if (emp.HasVersion(DataRowVersion.Original))
LastName = emp["LastName", DataRowVersion.Original].ToString();
if (emp.HasVersion(DataRowVersion.Proposed))
LastName = emp["LastName", DataRowVersion.Proposed].ToString();
emp.EndEdit();
rowState = emp.RowState; //Modified
if (emp.HasVersion(DataRowVersion.Current))
LastName = emp["LastName", DataRowVersion.Current].ToString();
if (emp.HasVersion(DataRowVersion.Original))
LastName = emp["LastName", DataRowVersion.Original].ToString();
if (emp.HasVersion(DataRowVersion.Proposed))
LastName = emp["LastName", DataRowVersion.Proposed].ToString();
// RowState LastName Current Original Proposed
// ========= ========== ======= ======== ========
//1)After BeginEdit Unchanged EEE EEE EEE FFF
//2)After EndEdit Modified FFF FFF EEE 無
轉換 DataTable 與 XML 資料
DataTable 轉換 XML 相關的方法
如何將 DataTable 轉成 XML 文件
DataTable employee = CreateDataTable();
employee.Rows.Add("987654321X", "vito", "shao", 40);
employee.Rows.Add("A123456789", "allen", "wu", 30);
employee.WriteXml(Server.MapPath("employee1.xml"));
Response.Redirect("employee.xml");
下面為輸出結果,其中,由於項目名稱不允許空白,所以自動使用 _x0020_ 取代。
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<Employee>
<EmpNo>987654321X</EmpNo>
<FirstName>vito</FirstName>
<LastName>shao</LastName>
<Age>40</Age>
<LastName_x0020_and_x0020_FirstName>shao,vito</LastName_x0020_and_x0020_FirstName>
</Employee>
<Employee>
<EmpNo>A123456789</EmpNo>
<FirstName>allen</FirstName>
<LastName>wu</LastName>
<Age>30</Age>
<LastName_x0020_and_x0020_FirstName>wu,allen</LastName_x0020_and_x0020_FirstName>
</Employee>
</DocumentElement>
如何控制 XML 文件的樣式
變更 DataColumn 物件的 ColumnMapping 屬性,可以控制 XML 文件輸出的樣式
MappingType 列舉值:
- Attribute :將欄位對應至 XML 的屬性 (Attribute)。
- Element :將欄位對應至 XML 的項目。(預設)
- Hidden :不對應欄位。
- SimpleContent :將欄位對應至 XmlText 的節點。
DataTable employee = CreateDataTable();
employee.Rows.Add("987654321X", "vito", "shao", 40);
employee.Rows.Add("A123456789", "allen", "wu", 30);
DataColumnCollection dColumns = employee.Columns;
dColumns["EmpNo"].ColumnMapping = MappingType.Attribute; //將 EmpNo 以屬性型式輸出
dColumns["FirstName"].ColumnMapping = MappingType.Hidden; //忽略輸出 FirstName 欄位
dColumns["LastName"].ColumnMapping = MappingType.Hidden; //忽略輸出 LastName 欄位
dColumns["Age"].ColumnMapping = MappingType.Hidden; //忽略輸出 Age 欄位
dColumns["LastName and FirstName"].ColumnName = "FullName";
employee.WriteXml(Server.MapPath("employee2.xml"));
Response.Redirect("employee2.xml");
下面為輸出結果:
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<Employee EmpNo="987654321X">
<FullName>shao,vito</FullName>
</Employee>
<Employee EmpNo="A123456789">
<FullName>wu,allen</FullName>
</Employee>
</DocumentElement>
如何將 DataTable 轉成含 Schema 的 XML 文件
上面的例子,我們輸出的 XML 文件並沒有包含資料的型別,所以所有的欄位資料都會被認定為字串。 若要在輸出的 XML 文件包含 Schema 的話,只要在輸出時,多加指定 XmlWriteMode 屬性即可。
DataTable employee = CreateDataTable();
employee.Rows.Add("987654321X", "vito", "shao", 40);
employee.Rows.Add("A123456789", "allen", "wu", 30);
DataColumnCollection dColumns = employee.Columns;
dColumns["EmpNo"].ColumnMapping = MappingType.Attribute; //將 EmpNo 對應成屬性
dColumns["LastName and FirstName"].ColumnMapping = MappingType.Hidden; //忽略 FullName 欄位
employee.WriteXml(Server.MapPath("employee3.xml"), XmlWriteMode.WriteSchema);
Response.Redirect("employee3.xml");
下面為輸出結果:
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="Employee" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element name="FirstName" msdata:Ordinal="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="LastName" msdata:Ordinal="2">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="Age" type="xs:short" default="0" minOccurs="0" msdata:Ordinal="3" />
</xs:sequence>
<xs:attribute name="EmpNo" msdata:DefaultValue="" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="10" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="LastName_x0020_and_x0020_FirstName" msdata:ReadOnly="true" msdata:Expression="LastName + ',' + FirstName" use="prohibited">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="40" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:unique name="Constraint1" msdata:PrimaryKey="true">
<xs:selector xpath=".//Employee" />
<xs:field xpath="@EmpNo" />
</xs:unique>
</xs:element>
</xs:schema>
<Employee EmpNo="987654321X">
<FirstName>vito</FirstName>
<LastName>shao</LastName>
<Age>40</Age>
</Employee>
<Employee EmpNo="A123456789">
<FirstName>allen</FirstName>
<LastName>wu</LastName>
<Age>30</Age>
</Employee>
</NewDataSet>
將 XML 文件轉 DataTable
透過 DataTable.ReadXml 方法,可以輕易的將 XML 文件轉入到 DataTable 。 若這個 XML 文件不含 shcema ,則必須先建立 DataTable 的樣式,而且要注意,讀取資料時,若資料型別無法對應,也會引發例外狀況。 若使用含 shcema 的 XML 文件,則可以不用建立 DataTable 的樣式,就可以直接取得和原本一模一樣的 DataTable 物件。
public XmlReadMode ReadXml(Stream stream); public XmlReadMode ReadXml(string fileName); public XmlReadMode ReadXml(TextReader reader); public XmlReadMode ReadXml(XmlReader reader);
protected void btnReaXML1_Click(object sender, EventArgs e)
{
DataTable Employee = CreateDataTable(); //先建立 DataTable 樣式
Employee.ReadXml(Server.MapPath("employee1.xml")); //讀取原始的 XML 文件
gv1.DataSource = Employee;
gv1.DataBind();
}
protected void btnReaXML2_Click(object sender, EventArgs e)
{
DataTable xmlTable = new DataTable(); //不須先建立 DataTable 樣式
xmlTable.ReadXml(Server.MapPath("employee3.xml")); //可讀取樣式的 XML 文件
gv1.DataSource = xmlTable;
gv1.DataBind();
}
使用 DataView 物件
DataView 物件
DataView 就像是 DataTable 的檢視表。主要功能可以用它來排序、篩選、搜尋資料。
DataView 建構子
public DataView(); public DataView(DataTable table); public DataView(DataTable table, string RowFilter, string Sort, DataViewRowState RowState);
DataView 屬性
- AllowDelete :是否允許刪除。
- AllowEdit :是否允許編輯。
- AllowNew :是否可以使用 AddNew 方法加入新的資料列。
- ApplyDefaultSort :是否要使用預設排序。若設為 true 則使用 PK 欄位做遞增排序。
- RowFilter :指定欄位篩選條件。
- RowStateFilter :指定 RowState 做為篩選條件。
- Sort :指定排序的欄位。
- DataTable :取得或設定來源 DataTable。
DataView 方法
排序 DataView
- 若設定 ApplyDefaultSort = true ,會使用 PK 欄位做遞增排序。
- 若有指定 Sort 屬性,則會依據該條件排序。
DataTable employee = CreateDataTable();
employee.Rows.Add("222", "vito", "shao", 40);
employee.Rows.Add("333", "allen", "wu", 30);
employee.Rows.Add("111", "shao", "vito", 35);
DataView myDataView = new DataView(employee);
myDataView.ApplyDefaultSort = true;
GridView1.DataSource = myDataView;
GridView1.DataBind();
DataView myDataView = new DataView(employee); myDataView.Sort = "FirstName DESC, Age ASC"; GridView1.DataSource = myDataView; GridView1.DataBind();
篩選 DataView
DataView.RowFilter 屬性是用來過濾資料用,其使用語法類似 SQL 中的 Where 子句,但是不用 Where 這個關鍵字。
DataView myDataView = new DataView(employee); myDataView.RowFilter = " Age > 30 and FirstName like '%A%' "; myDataView.Sort = "FirstName DESC"; GridView1.DataSource = myDataView; GridView1.DataBind();
搜尋 DataView
使用 Find 方法前,必須先指定排序的欄位,因為它是依照排序的欄位進行搜尋,並回傳第一筆符合條件資料的索引。
protected void btnFind_Click(object sender, EventArgs e)
{
DataTable employee = CreateDataTable();
employee.Rows.Add("222", "vito", "shao", 40);
employee.Rows.Add("333", "allen", "wu", 30);
employee.Rows.Add("111", "shao", "vito", 35);
DataView myDataView = new DataView(employee);
myDataView.Sort = "Age ASC";
int iRowIndex = myDataView.Find("30");
if (iRowIndex == -1)
{
myDebug.WriteLine("找不到符合條件的資料");
}
else
{
DataTable dTable = CreateDataTable();
DataRow dRow = dTable.NewRow();
foreach (DataColumn col in employee.Columns)
{
if (!col.ReadOnly)
dRow[col.ToString()] = myDataView[iRowIndex][col.ToString()];
}
dTable.Rows.Add(dRow);
GridView1.DataSource = dTable;
GridView1.DataBind();
}
}
使用多欄位條件搜尋
myDataView.Sort = "FirstName, LastName"; // Find FirstName + LastName object[] vals = new object[2]; vals[0] = "vito"; vals[1] = "shao"; int iRowIndex = myDataView.Find(vals);
使用 DataSet 物件
DataSet 物件
DataSet 是一種使用記憶體來存放關聯性資料的物件。其中包含了 DataTable 和 DataRelation 物件。 透過 DataRelation 物件可以用來建立兩個 DataTable 之間的父子關聯性。
如何建立 DataSet 與 DataTable 與 DataRelation
private DataSet CreateDataSet()
{
//==========================================================
// 在 DataSet 中,加入 DataTable
//==========================================================
DataTable dtCompany = new DataTable("Company");
dtCompany.Columns.Add("CompanyId", typeof(Guid));
dtCompany.Columns.Add("CompanyName", typeof(string));
dtCompany.PrimaryKey = new DataColumn[] { dtCompany.Columns["CompanyId"] };
DataTable dtEmployee = new DataTable("Employee");
dtEmployee.Columns.Add("EmployeeId", typeof(Guid));
dtEmployee.Columns.Add("CompanyId", typeof(Guid));
dtEmployee.Columns.Add("LastName", typeof(string));
dtEmployee.Columns.Add("FirstName", typeof(string));
dtEmployee.Columns.Add("Salary", typeof(decimal));
dtEmployee.PrimaryKey = new DataColumn[] { dtEmployee.Columns["EmployeeId"] };
DataSet dsCompanyData = new DataSet("CompanyList");
dsCompanyData.Tables.Add(dtCompany);
dsCompanyData.Tables.Add(dtEmployee);
//==========================================================
// 在 DataSet 中,加入 DataRelation
//==========================================================
DataRelation rlCompanyEmployee =
new DataRelation("Company_Employee", dtCompany.Columns["CompanyId"], dtEmployee.Columns["CompanyId"]);
dsCompanyData.Relations.Add(rlCompanyEmployee);
return dsCompanyData;
}
如何將 DataSet 繫結到控制項
DataRelation 物件
DataRelation 是用來表示 DataTable 之間的關聯性。 透過 DataRelation 可以由 Parent DataTable 關聯到 Child DataTable ,反之亦可。
- DataRow.GetChildRows :取得關聯子資料表中相關聯的 DataRow 資料列。
//建立測試資料
DataSet companyList = CreateDataSet();
PopulateDataSet(companyList);
//取得資料關聯性
DataRelation dRelation = companyList.Relations["Company_Employee"];
//取出父資料表中的一筆公司資料
DataRow companyParent = companyList.Tables["company"].Rows[1];
//透過關連取出與該公司相關聯的員工資料
string tmp = "";
foreach (DataRow employeeChild in companyParent.GetChildRows(dRelation))
{
tmp = employeeChild["LastName"] + " " +
employeeChild["FirstName"] + " " +
string.Format("{0:C}", employeeChild["Salary"]);
myDebug.WriteLine(tmp);
}
建立條件約束 (Key Constraint)
若父資料表有一個 Primary Key ,通常我們會在子資料表建立 Foreing Key ,以便約束索引欄位的正確性。 或者當父資料表有資料被刪除時,我們希望子資料表中相關聯的資料也自動被刪除,類似這樣的需求都可以透過索引鍵條件約束 (Key Constraint) 來達成。
UniqueConstraint
UniqueConstraint 這個物件可以用來表示欄位值必須唯一的限制。
ForeignKeyConstraint
ForeignKeyConstraint 這個物件就是用來建立 Fk 的約束行為。以下是它的幾個方法:
- DeleteRule :設定資料列刪除時會跨越這個條件約束發生的動作。
- UpdateRule :設定資料列更新時會跨越這個條件約束發生的動作。
- AcceptRejectRule :指示在叫用 AcceptChanges 時應該跨越這個條件約束來發生的動作。
protected void btnCascadingUpdateDelete_Click(object sender, EventArgs e)
{
//父層資料表:建立 Unique Key
//子層資料表:建立 Foreign Key
DataSet companyList = CreateConstraintDataSet();
PopulateDataSet(companyList);
//刪除父層資料表中的資料,則子層資料表中的相關資料也會被刪除
companyList.Tables["Company"].Rows[0].Delete();
BindingDataSet(companyList);
}
private DataSet CreateConstraintDataSet()
{
DataTable dtCompany = new DataTable("Company");
dtCompany.Columns.Add("CompanyId", typeof(Guid));
dtCompany.Columns.Add("CompanyName", typeof(string));
dtCompany.PrimaryKey = new DataColumn[] { dtCompany.Columns["CompanyId"] };
DataTable dtEmployee = new DataTable("Employee");
dtEmployee.Columns.Add("EmployeeId", typeof(Guid));
dtEmployee.Columns.Add("CompanyId", typeof(Guid));
dtEmployee.Columns.Add("LastName", typeof(string));
dtEmployee.Columns.Add("FirstName", typeof(string));
dtEmployee.Columns.Add("Salary", typeof(decimal));
dtEmployee.PrimaryKey = new DataColumn[] { dtEmployee.Columns["EmployeeId"] };
//======================================================
// 建立 DataSet
//======================================================
DataSet dsCompanyData = new DataSet("CompanyList");
dsCompanyData.Tables.Add(dtCompany);
dsCompanyData.Tables.Add(dtEmployee);
//======================================================
// 建立 UniqueConstraint
//======================================================
//UniqueConstraint UKey = new UniqueConstraint("UK_CompanyId", dtCompany.Columns["CompanyId"]);
//dtCompany.Constraints.Add(UKey); // (注意:該欄位若為 PK , 則無法再建立 unique )
//======================================================
// 建立 ForeignKeyConstraint
//======================================================
ForeignKeyConstraint FKey = new ForeignKeyConstraint("Fk_CompanyId",
dtCompany.Columns["CompanyId"], dtEmployee.Columns["CompanyId"]);
FKey.DeleteRule = Rule.Cascade; //設定若父層刪除,子層也跟著刪除
FKey.UpdateRule = Rule.Cascade; //設定若父層變更,子層也跟著變更
FKey.AcceptRejectRule = AcceptRejectRule.Cascade;
dtEmployee.Constraints.Add(FKey);
return dsCompanyData;
}
序列化 DataSet 物件
- WriteXml :將目前 DataSet 中的資料寫入指定的 XML 檔案。若參數 XmlWriteMode 設成 WriteSchema ,可連同 DataSet 的結構描述一起輸出。
- WriteXmlSchema :將目前 DataSet 的結構描述寫入指定的 XML 檔案。
- ReadXml :讀取指定檔案中的 XML 結構描述和資料。
- ReadXmlSchema :讀取指定檔案中的 XML 結構描述。
將 DataSet 輸出成 XML 文件
//建立測試資料
DataSet companyList = CreateDataSet();
PopulateDataSet(companyList);
//輸出 DataSet 中的資料到 XML 文件
companyList.WriteXml(MapPath("CompanyList.xml"));
//輸出 DataSet 中的資料和結構描述到 XML 文件
companyList.WriteXml(MapPath("CompanyListSchema.xml"), XmlWriteMode.WriteSchema);
//輸出 DataSet 中的結構描述到 XML 文件
companyList.WriteXmlSchema(MapPath("CompanyListSchema.xsd"));
將 DataSet 輸出成巢狀的 XML 文件
上面的例子,其輸出的 XML 文件會把 Company 資料會全部集中在一起, Employee 資料也會集中在一起。 如果我們希望在某一筆公司資料底下顯示該公司的員工資料,就可以用設定成巢狀 (Nested) 的 XML 文件。 當然啦,這個功能是由 DataSet 中的 DataRelation 所控制的。
//建立測試資料
DataSet companyList = CreateDataSet();
PopulateDataSet(companyList);
//設定巢狀的 DataRelation
companyList.Relations["Company_Employee"].Nested = true;
//輸出
companyList.WriteXml(MapPath("CompanyList.xml"));
將異動過的 DataSe 輸出成 XML 文件
由前面的說明,我們知道, DataSet 中包含一個稱為 DataRowVersion 的資訊,可用來表示更改前後的不同資訊。 若希望輸出的 XML 文件包含所有的資料,則可以將參數 XmlWriteMode 設成 DiffGram 。
XmlWriteMode 列舉:
- WriteSchema :輸出內容包含資料和結構描述。
- IgnoreSchema :輸出內容僅含資料。
- DiffGram :輸出內容包括原始和目前的值。
範例:
//建立測試資料
DataSet companyList = CreateDataSet2();
PopulateDataSet2(companyList);
companyList.Tables["Employee"].AcceptChanges();
companyList.Tables["Employee"].Rows[1]["Salary"] = 0;
// XML 文件
companyList.WriteXml(MapPath("Employee.xml"));
// DiffGram XML 文件
companyList.WriteXml(MapPath("EmployeeDiffGram.xml"), XmlWriteMode.DiffGram);
下面為輸出結果:
第10行顯示該筆資料的狀態為 Modified 。
而文件的下半部,則顯示未更改的原始資料。
<?xml version="1.0" standalone="yes"?>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<CompanyList>
<Employee diffgr:id="Employee1" msdata:rowOrder="0">
<EmployeeId>001</EmployeeId>
<LastName>AAA</LastName>
<FirstName>aaa</FirstName>
<Salary>40</Salary>
</Employee>
<Employee diffgr:id="Employee2" msdata:rowOrder="1" diffgr:hasChanges="modified">
<EmployeeId>002</EmployeeId>
<LastName>BBB</LastName>
<FirstName>bbb</FirstName>
<Salary>0</Salary>
</Employee>
<Employee diffgr:id="Employee3" msdata:rowOrder="2">
<EmployeeId>003</EmployeeId>
<LastName>CCC</LastName>
<FirstName>ccc</FirstName>
<Salary>20</Salary>
</Employee>
</CompanyList>
<diffgr:before>
<Employee diffgr:id="Employee2" msdata:rowOrder="1">
<EmployeeId>002</EmployeeId>
<LastName>BBB</LastName>
<FirstName>bbb</FirstName>
<Salary>70</Salary>
</Employee>
</diffgr:before>
</diffgr:diffgram>
由上面例子可以知道 DiffGram 會輸出整個 DataSet 中的資訊,包含變更過和沒變更過的資料。 若只希望輸出的 XML 文件僅含變更過的資訊,可以使用 GetChanges 方法,取得變更資料集的複本,再輸出成 XML 文件。
還原序列化 DataSet 物件
//讀取結構描述
DataSet companyList1 = new DataSet();
companyList1.ReadXmlSchema(MapPath("CompanyListSchema.xml"));
//讀取資料檔
companyList1.ReadXml(MapPath("CompanyListData.xml"));
//同時讀取結構描述和資料檔
DataSet companyList2 = new DataSet();
companyList2.ReadXml(MapPath("CompanyListDataSchema.xml"));
二進位序列化與還原序列化 DataSet 物件
- 二進位序列化約有 20K 的初始化額外資訊,所以序列化小型的 DataSet 時,檔案不一定會比較小。
- 二進位序列化會自動包含結構描述,所以還原序列化時不需要先載入結構描述。
// 使用 BinaryFormatter 序列化 DataSet
protected void btnBinarySerializing_Click(object sender, EventArgs e)
{
DataSet companyList = CreateDataSet();
PopulateDataSet(companyList);
// 設定輸出成二進位格式
companyList.RemotingFormat = SerializationFormat.Binary;
using (FileStream fs = new FileStream(MapPath("CompanyList.bin"), FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, companyList);
}
}
// 使用 BinaryFormatter 還原序列化 DataSet
protected void btnBinaryDeserializing_Click(object sender, EventArgs e)
{
DataSet companyList;
using (FileStream fs = new FileStream(MapPath("CompanyList.bin"), FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
companyList = (DataSet)formatter.Deserialize(fs);
}
BindingDataSet(companyList);
}
合併 DataSet
public void Merge(DataRow[] rows); public void Merge(DataSet dataSet); public void Merge(DataTable table); public void Merge(DataSet dataSet, bool preserveChanges); public void Merge(DataSet dataSet, bool preserveChanges, MissingSchemaAction missingSchemaAction);
- dataSet:合併對象。
當使用 A.Merge(B) 時,表示 A 資料集合併 B 資料集,B 資料集的資料將被併入到 A 資料集中。 - preserveChanges:若要保留目前 DataSet 中的變更,則為 true,否則為 false。
當 key 重複時,預設會將 B 資料集中的資料覆寫到 A 資料集中。
若希望保留 A 資料集的資料,不希望被覆寫,設定 preserveChanges 參數為 true 即可。 - missingSchemaAction:合併時,若發生欄位不相符時的處理原則。
DataSet companyList1 = CreateDataSet2(); PopulateDataSet2(companyList1); DataSet companyList2 = CreateDataSet2(); PopulateDataSet3(companyList2); companyList1.AcceptChanges(); companyList2.AcceptChanges(); companyList1.Tables["Employee"].Rows[0]["Salary"] = 0; companyList1.Merge(companyList2); //雖然 companyList1 資料中的 Salary 已改成 0, 但合併後,會被 companyList2 覆寫 companyList1.Merge(companyList2, true); //若 preserveChanges 設成 true , 則不會覆寫
如何由現有的 Datatable 產生 Group By 的 Datatable
- 1. 先建立 Group By 欄位的 Datatable
- 2. 再透過 Relaction 欄位求加總或平均。
下面例子,假說有個員工資料表,你希望依據現有 DataTable 中的資料,以 FirstName 為群組欄位,求得平均年齡。
//原始資料
DataTable employees = TestData.GetEmployeeDataTable();
//利用原始資料, 建立一個 Distinct FirstName 欄位的 DataTable
DataTable grouped = employees.DefaultView.ToTable("GEmployee", true, new[] { "FirstName" });
DataSet ds = new DataSet("myDataSet");
ds.Tables.Add(employees);
ds.Tables.Add(grouped);
//建立這二個 datatable 的關連
ds.Relations.Add("Relation1", grouped.Columns["FirstName"], employees.Columns["FirstName"]);
//藉由關連欄位,取得子表單的總計 Child 是關鍵字
grouped.Columns.Add("AvgAge", typeof(int), "Avg(Child.Age)");
如何具名 DataSet 中的 DataTable
當透過查詢取得一個含有多個 DataTable 的 DataSet ,通常我們會使用索引值取得每一個 DataTable , 如果你想要讓每一個 DataTable 都有一個名字,可以使用以下的方法:
取得 DataSet 後為每個 DataTable 命名
ds.Tables[0].TableName = "Orders";
ds.Tables[1].TableName = "OrderDetails";
建立 DataTable 對應表
myDataAdapter.TableMappings.Add("Table1", "Orders");
myDataAdapter.TableMappings.Add("Table2", "OrderDetails");
myDataAdapter.Fill(myDataSet);

沒有留言:
張貼留言