ADO.Net类可以分为以下两大类:
1 非连接类:DataSet/DataTableCollection/DataTable/DataRowCollection/DataColumnCollection/DataRelationCollection ßàXML
2 连接类:
DataAdapter(SelectCommand/InsertCommand/DeleteCommand/UpdateCommand)
DataReader
Connectionßà数据存储区
非连接类是客户在应用程序中进行实例化的,也就是说,即使没有连接数据存储区,也是可以使用非连接类的。
//creat the datetable named "Auto"
DataTable auto = new DataTable("Auto");//Auto 是auto对象的TableName的值
表1 DataColumn对象的常用属性及其默认值
属性 |
默认值 |
DataType |
String |
MaxLength |
-1 不会对最大长度执行检查 |
Unique |
False |
AllowDBNull |
Ture |
Caption |
默认为该对象的Name属性值 |
//Add the DataColumn using all properties
DataColumn vin = new DataColumn("Vin");
vin.DataType = typeof(string);
vin.MaxLength = 23;
vin.Unique = true;
vin.AllowDBNull = false;
vin.Caption = "VIN";
auto.Columns.Add(vin);
//Add the DataColumn using defaults
DataColumn make = new DataColumn("Make");
make.MaxLength = 35;
make.AllowDBNull = false;
auto.Columns.Add(make);
DataColumn year = new DataColumn("Year", typeof(int));
year.AllowDBNull = false;
auto.Columns.Add(year);
//using expression
DataColumn yearmake = new DataColumn("Year and Make");
yearmake.DataType = typeof(string);
yearmake.MaxLength = 70;
yearmake.Expression = "Year" + " "+"Make";
auto.Columns.Add(yearmake);
备注:需要注意的地方均用下划线标注
说明:一个Table的主键可能是1列,也可能是多列的集合。因此,PrimaryKey必须设置为一个DataColumn对象数组,以容纳复合主键。
//set primary key
auto.PrimaryKey = new DataColumn[] { vin };
DataTable对象包含一个Rows集合,通常用Add方法和Load方法两种方法将DataRow对象插入到Rows集合中去。
//Add new row bu creating the datarow first
DataRow newauto = auto.NewRow();
newauto ["Vin"]="123456789abcd"
newauto ["Make"]="QiRui";
newauto ["Year"]=2007;
auto.Rows.Add(newauto);
//add new datarow bu simply adding the values
auto.Rows.Add("987654321dcba","Buick",2006);
//load datarow,replacing existing contents ,if existing
auto.LoadDataRow(new object[]{"98654321","Jeep",2004},LoadOption.OverwriteChanges);
通过查看DataRow对象的RowState属性可以确定该DataRow对象的状态。DataRow的对象状态有一下五种:Detached/Added/Unchanged/Modified/Deleted。
DataRow对象可以包含以下三个版本的数据:Original/Current/Proposed。如下表所示各版本数据的变换迁徙:
表2 各版本数据的变换迁徙
动作 |
Original |
Current |
Proposed |
1加载 |
|
50 |
|
2BeginEdità编辑模式 |
|
50 |
60 (new) |
3EndEdità编辑模式 |
50 |
60 |
|
4BeginEdità编辑模式 |
50 |
60 |
70(new) |
5EndEdità编辑模式 |
50 |
70 |
|
注意1:在编辑DataRow对象的值时,要结合当前DataRow对象的DataRowState状态;
注意2:DataRow对象包含一个HasVersion方法,可用于查询某个特定的DataRowVersion的值是否存在。如下例所示:
private string GetDataRowInfo(DataRow row, string columnName)
{ string retVal = string.Format("RowState:{0} /r/n",row.RowState);
foreach (string versionString in Enum.GetNames(typeof(DataRowVersion)))
{ DataRowVersion version = (DataRowVersion)Enum.Parse(typeof(DataRowVersion), versionString);
if (row.HasVersion(version))
{
retVal += string.Format("Version:{0} Value:{1} /r/n", versionString, row[columnName, version]);
}
else
{
retVal += string.Format("Version:{0} does not exist. /r/n", versionString);
}
}
return retVal;
}
1) AcceptChanges:可以将DataRow对象的RowState重新置为Unchanged;
2) RejectChanges:将使DataRow对象回到上一次调用AcceptChanges方法时的状态;RowState重新置为Unchanged
3) SetAdded:将RowState置为Added
4) SetModified:将RowState置为Modified,但是3)4)方法都要求RowState为UnChanged,才可以使用该方法。
5) Delete:将RowState置为Deleted,但是这时需要调用AcceptChanges方法,才能真正删除该Row,若不删除该Row,只有调用RejectChanges方法。
使用foreach语句即可,见下例:
private string ListDataTale (DataTable dt)
{
StringBuilder buffer = new StringBuilder();
foreach (DataColumn dc in dt.Columns)
{
buffer.Append(string.Format("{0,15} ", dc.ColumnName));
}
buffer.Append("/r/n");
foreach (DataRow dw in dt.Rows)
{
foreach (DataColumn dc in dt.Columns)
{
buffer.Append(string.Format("{0,15} ", dw[dc]));
}
buffer.Append("/r/n");
}
return buffer.ToString();
}
1) Copy方法:该方法复制DataTable对象的模式和数据;
2) Clone方法:只复制DataTable对象的模式;
3) ImportRow方法:在Clone后,往往需要从原表中复制一部分数据,这时候可以使用该方法。
示例: DataTable copy=auto.Copy();
DataTable clone = auto.Clone();
clone.ImportRow(auto.Rows[0]);
1) auto.WriteXml(@"d:/auto.xml");
2) 使用ColumnMapping属性控制列XML的输出
auto.TableName = "Car";
auto.Columns["Vin"].ColumnMapping = MappingType.Attribute;
auto.Columns["Make"].ColumnMapping = MappingType.Attribute;
auto.Columns["Year"].ColumnMapping = MappingType.Attribute;
auto.Columns["Year and Make"].ColumnMapping = MappingType.Hidden;
auto.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/car.xml"
3)使用XmlWriteMode枚举值将模式和数据一起保存到XML文件
auto.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/car1.xml",XmlWriteMode.WriteSchema);
DataTable xmltable = new DataTable(); xmltable.ReadXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/car1.xml");
DataView对象为DataTable对象提供了一个窗口,可以存储和过滤DataView对象。可以为一个DataTable对象分配多个DataView对象,并且允许以多种不同的方式查看数据,而不必从数据库中读取数据。
DataView view = new DataView(auto);
view.Sort = "Make ASC,Year DESC";
RowFilter中的是过滤条件;
RowStateFilter需要使用DataViewRowState枚举值,而且该枚举是一个位标记枚举,可以对这些枚举成员使用按位OR运算符来获得过滤多种状态。
DataView view = new DataView(auto);
view.Sort = "Make ASC,Year DESC";
view.RowFilter = "Make like 'B%' and Year>2003";
view.RowStateFilter = DataViewRowState.Added ^ DataViewRowState.Unchanged;
private string ListDataView(DataTable dt, DataView view)
{
StringBuilder buffer = new StringBuilder();
foreach (DataColumn dc in dt.Columns)
{
buffer.Append(string.Format("{0,15} ", dc.ColumnName));
}
buffer.Append("/r/n");
foreach (DataRowView drv in view)
{
foreach (DataColumn dc in dt.Columns)
{
buffer.Append(string.Format("{0,15} ", drv.Row[dc]));
}
buffer.Append("/r/n");
}
return buffer.ToString();
}
DataTable export = view.ToTable("NewAutoTable",true,"Vin","Make","Year");
注意:布尔值true用于表示应显示不同的值(排除重复值)还是应显示所有的值;各参数的意义可以在IDE环境中获得帮助!
DataSet对象是数据的一种基于内存的关系表示,是主要的非连接数据对象。可以将DataSet视为内存中的一种关系数据库,但是它仅仅缓存数据,而不提供任何对于当前关系数据库至关重要的事务属性。
DataSet对象包括一个DataTable对象集合和一个DataRelation对象集合。DataSet对象可以包含惟一外键约束,以保证数据的完整性。
通过编程或者通过提供XML模式定义,都可以创建DataSet模式。
示例1:通过编程实现
DataSet vendorData = new DataSet("VendorData");
DataTable vendor = vendorData.Tables.Add("Vendor");
vendor.Columns.Add("ID", typeof(Guid));
vendor.Columns.Add("Name",typeof(string));
vendor.Columns["Name"].MaxLength = 50;
vendor.Columns.Add("Address1", typeof(string));
vendor.Columns.Add("Address2", typeof(string));
vendor.Columns.Add("City", typeof(string));
vendor.Columns.Add("State", typeof(string));
vendor.Columns.Add("ZipCode", typeof(string));
vendor.Columns.Add("Country", typeof(string));
vendor.PrimaryKey = new DataColumn[] { vendor.Columns["ID"] };
DataTable part = vendorData.Tables.Add("Part");
part.Columns.Add("ID", typeof(Guid));
part.Columns.Add("VendorID", typeof(Guid));
part.Columns.Add("PartCode", typeof(string));
part.Columns.Add("PartDescription", typeof(string));
part.Columns.Add("Cost", typeof(decimal));
part.PrimaryKey = new DataColumn[] { part.Columns["ID"] };
vendorData.Relations.Add("vendor_part",vendor.Columns["ID"], part.Columns["VendorID"]);
上面的代码创建的模式,我们可以这样去访问:vendorData.Tables[“Vendor”];但是如果拼写表明发生错误,特别是大写的错误,只有在运行时才会抛出一个异常。一个更好的解决方案是通过继承DataSet类,创建一个新的、专业化的子类,并且为每个表添加一个属性。例如,一个专业化的DataSet子类可能包含一个名为Vendor的属性,可以按照以下方式访问该属性:
示例:
public class MyDataSet : DataSet
{
private DataTable student = new DataTable("Student");
public DataTable Student
{
get
{
return student;
}
}
public MyDataSet():base()
{
student.Columns.Add("ID",typeof(Guid));
//...
}
}
这样就可以这样访问:
MyDataSet mydataset = new MyDataSet();
DataTable dt = mydataset.Student;
上节的例子是一种新建一个类型化的DataSet类的示例,但是在.Net中,手动创建一个类型化的DataSet类的最好方法是提供一个可用于生成类型化DataSet类的XML模式定义文件(XSD)文件。
VisualStadio提供了一个名为DataSet编辑器的工具,用户可以使用该工具图形化地创建和修改一个XSD文件,而后使用该XSD文件生成一个类型化的DataSet类。
添加方法如下:
1) 添加新项;
2) 选择DataSet模板;
3) 编辑该类型DataSet;
使用方法:
上面的步骤创建的文件包就是生成了一个专业化的类型化的DataSet类,可以和使用一个类一样使用它。如:
MyDataSetFromXSD mydatasetfromxsd = new MyDataSetFromXSD();
DataTable dt = mydatasetfromxsd.Teacher;
//使用DataRelation
DataRow vendorRow = vendor.NewRow();
Guid vendorID = Guid.NewGuid();
vendorRow["ID"] = vendorID;
vendorRow["Name"] = "speed";
vendor.Rows.Add(vendorRow);
DataRow partRow = part.NewRow();
partRow["ID"] = Guid.NewGuid();
partRow["VendorID"] = vendorID;
partRow["PartCode"] = "123234";
partRow["PartDescription"] = "dfasfasf";
partRow["Cost"] = 10.0;
part.Rows.Add(partRow);
//导航 parent to child
DataRow[] parts = vendorRow.GetChildRows("vendor_part");//重点代码
foreach (DataRow dr in parts)
{
…………
}
//导航 child to parent
DataRow parentRow = part.Rows[0].GetParentRow("vendor_part");//重点代码
可以创建一个具有或没有惟一外键约束的DataRelation对象,以在父子DataTable对象之间实现导航。DataRelation对象还包含一个构造函数,该函数不仅允许在父表对象上创建一个惟一约束,还允许在子DataTable对象上创建一个外键约束。这样的约束保证了数据完整性。
vendorData.Relations.Add("vendor_part",vendor.Columns["ID"], part.Columns["VendorID"],true); //这里的true表示创建约束。
注意:如果一个外键约束设置在一个允许null值的列上,则可以存在没有父DataRow对象的子DataRow对象。有些情况可能希望或要求这么做。但是,大多数情况下,都要求保持数据的完整性,因此,一定要验证作外键的列的AllowDbNull的属性。
外键约束确保了数据的完整性,但是,很多情况我们希望在删除父记录时,强制删除它的子记录。或是希望将父记录中的惟一键修改时,它所对应的子记录的外键能够强制更新。
可以设置ForeignKeyConstraint对象的DeleteRule属性值来确定级联删除的方式;设置ChangeRule属性的值来获得相应的行为。
示例:级联删除和级联更新
ForeignKeyConstraint fk = (ForeignKeyConstraint)part.Constraints["vendor_part"];
fk.DeleteRule = Rule.None;//删除一个拥有子记录的父记录时,或添加一个没有父记录的子记录时,强行抛出一个异常
fk.UpdateRule = Rule.None;
可以将DataSet对象以XML数据或二进制数据的形式保存或序列化到一个流或文件中。还可以从一个流或文件解序到一个DataSet对象。这样做的好处是这个流或文件可以通过多种协议在网络中间进行传输。
注意:但凡是使用WriteXMl方法序列化对象时,我们都可以通过设置MappingType的值来选择序列化的数据和方式。
示例:
//序列化DataSet对象至XML文件 vendorData.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData.xml");
//另一种序列化的格式:这种是将Part元素嵌套在拥有它的Vendor元素内
vendorData.Relations["vendor_part"].Nested = true;
foreach (DataTable dt in vendorData.Tables)
{
foreach (DataColumn dc in dt.Columns)
{
if (dc.DataType != typeof(Guid))
{
dc.ColumnMapping = MappingType.Attribute;
}
}
} vendorData.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_nested.xml");
//上面的方法没有序列化DataSet对象的模式信息,可以使用下面的方式: vendorData.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_nested_Schema.xml",XmlWriteMode.WriteSchema);
注意:使用WriteSchema方法的时候,在数据不多的时候,生成的XML的大小还是可以接受的。但是,如果数据很多时,生成的XML文件将会变得很庞大。如上例,没有使用WriteSchema方法方法生成的文件大小为1K,而使用WriteSchema方法生成的文件大小为3K。处理这种情况的方法是创建一个可在加载数据之前单独进行加载的XSD文件。如下例:
vendorData.WriteXmlSchema(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_Schema.xsd");
DiffGram是包含该DataSet对象中所有的数据(包括DataRow对象的原有信息)的XML文档。只需要将XmlWriteMode模式设置为DiffGram即可。如:vendorData.WriteXml(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_nested_DiffGram.xml", XmlWriteMode.DiffGram);
主要有两个方法:
1. ReadXmlSchema 读取模式xsd文件;
2. ReadXml 读取一个XML文件,如果已经读取过模式的话,可以将XmlReadMode设置成IngoreSchema。具体的枚举值所代表的含义可以在VS开发环境的帮助方面的获得。
在许多情况下,如果通过网络移动数据,则XML文件的大小会产生诸如内存、驱动器空间和网络带宽等资源问题。这时候,可以将DataSet对象序列化为二进制数据。
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
//将DataSet对象序列化为二进制数据,但该二进制文件包含嵌套的XML文件 BinaryXML
BinaryFormatter fmt = new BinaryFormatter();
FileStream fs = new FileStream(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData.bin",FileMode.Create);
fmt.Serialize(fs, vendorData);
fs.Close();
//将该文件保存为真正的二进制数据 TrueBinary
vendorData.RemotingFormat = SerializationFormat.Binary;
FileStream fs1 = new FileStream(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_really.bin",FileMode.Create);
fmt.Serialize(fs1, vendorData);
fs.Close();
这些Bin文件可以直接用VS打开查看。
BinaryFormatter fmt = new BinaryFormatter();
DataSet temp;
FileStream fs2 = new FileStream(@"D:/c#_database_eg/datatable_eg/datatable_eg/XML/vendorData_really.bin", FileMode.Open);
temp = (DataSet)fmt.Deserialize(fs2);
fs2.Close();
在许多情况下,一个DataSet对象中的可用的数据必须和另一个DataSet对象中的数据进行合并。DataSet对象包含一个名为Merge的方法来合并多个DataSet对象的数据。该方法允许合并DataSet、DataTable、DataRow对象中的数据。
示例:
//create an inital DataSet
DataSet masterData = new DataSet("Sales");
DataTable person =masterData.Tables.Add("Person");
person.Columns.Add("ID", typeof(Guid));
person.Columns.Add("Name", typeof(string));
person.PrimaryKey = new DataColumn[] { person.Columns["ID"] };
person.Rows.Add(Guid.NewGuid(), "GoodSpeed");
//create a temp DataSet and make changes
DataSet tempData = masterData.Copy();
//get goodspeed's infomation
DataTable tempperson = tempData.Tables["Person"];
DataRow goodspeed = tempperson.Select("Name='GoodSpeed'")[0];
Guid goodspeedID = (Guid)goodspeed["Id"];
//modify goodspeed's name
goodspeed["Name"] = "zhailei";
//create an order table and add orders for zhailei
DataTable order = tempData.Tables.Add("Order");
order.Columns.Add("ID", typeof(Guid));
order.Columns.Add("PersonID", typeof(Guid));
order.Columns.Add("Amount", typeof(decimal));
order.PrimaryKey = new DataColumn[] { order.Columns["ID"] };
order.Rows.Add(Guid.NewGuid(), goodspeedID, 100.00);
//now ,merge back to master
masterData.Merge(tempData, false, MissingSchemaAction.AddWithKey);
注意:false参数用于指定tempdata中的更新会覆盖masterData对象中的修改,用true,则tempdata中的更新不会覆盖masterData对象中的对应数据。
该对象使我们能够遍历一个或多个DataTable对象中的DataRow对象。它提供一种稳定的,只进只读的方法来遍历DataRows对象。同时,我们还可以使用该对象来填充多个Windows和Web控件,而不用编写遍历代码,节约代码编写的时间。
//create an inital DataSet
DataSet masterData = new DataSet("Sales");
DataTable person = masterData.Tables.Add("Person");
person.Columns.Add("ID", typeof(Guid));
person.Columns.Add("Name", typeof(string));
person.PrimaryKey = new DataColumn[] { person.Columns["ID"] };
DataTable part = masterData.Tables.Add("Part");
part.Columns.Add("ID", typeof(Guid));
part.Columns.Add("PartName", typeof(string));
part.PrimaryKey = new DataColumn[] { part.Columns["ID"] };
//add data
for (int i = 0; i < 10; i++)
{
person.Rows.Add(Guid.NewGuid(), "GoodSpeed" + i.ToString());
part.Rows.Add(Guid.NewGuid(), "partzhailei" + i.ToString());
}
//read the data from the table
txt1.Text = "DataReader" + "/r/n";
DataTableReader rd = masterData.CreateDataReader();
while (rd.Read())
{
txt1.AppendText(rd["Name"].ToString() + "/r/n");
}
rd.NextResult(); //使dataTableReader移动到下一个结果集,如果存在的话
while (rd.Read())
{
txt1.AppendText(rd["PartName"].ToString() + "/r/n");
}