http://www.poluoluo.com/jzxy/200910/66911.html
1 前言
目前,Access+ASP.NET模式广泛应用于中小型网站的构建,MDB文件是Access数据库文件,是该模式下网站的数据源。DBF文件是微软桌面数据库软件FoxPro的数据库文件,以其简单易学、操作方便等优点仍然广泛应用于高校的招生、学籍、就业等教学管理环节中,如上级部门下达的数据库文件、高校给上级部门的上报数据文件仍然是DBF文件。由此可见,基于MDB文件的教学管理系统在数据源的格式方面就与日常报表所用的DBF文件存存不一致性,要解决这个问题通常可借助于第三软件(如Microsoft Office系统提供的Access组件或专用的共享软件)来完成文件格式的转换,即数据的转换具有一定的依赖性。作为一个应用系统,应尽量减少这种依赖性,让用户在应用中直接使用DBF文件,因而要求我们开发的应用系统能自动地完成DBF文件向MDB文件的转换,这就是本文要解决的问题。
2 工作原理
ADO.NET是.NET平台上数据库访问技术之一,该技术提供对数据库读写、显示等功能组件,如DataReader、DataAdapter、DataSet等,借助于DataReader对DBF文件实现数据以行记录方式读出,以DataAdapter作为DataSet与数据源(MDB文件)的数据适匹器,调用DataAdapter的Fill方法填充DataSet得到一个DataTable(数据表),通过调用DataAdapter的Update方法将变化了的DataSet数据更新源数据(MDB文件),从而实现DBF向MDB文件的转换。
3 实现过程
实现的过程可分为三个主要步骤:创建MDB文件→在MDB文件中创建数据表(即创建表结构)→往数据表添加数据。在这三个主要步骤中均需要使用ADO.NET技术连接DBF文
件,ASP.NET下利用C#连接DBF的关键代码可表示为:
OdbcConnection conn = new OdbcConnection();
string connStr = @"Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" +
dbfFileName + ";Exclusive=No;NULL=NO;Collate=Machine;BACKGROUNDFETCH=NO;DELETED=NO";
sqlString = "select * from " + dbfFileName;
conn.ConnectionString = connStr;
conn.Open();
其中dbfFileName表示待连接的DBF数据库(含路径和扩展名),实际应用时以上代码可封装成一个方法,将方法类型定义为OdbcConnection,即方法返回值为OdbcConnection类型,方法的形参为string类型,代表待连接的DBF数据库文件名。下面为方法的定义与调用代码:
//定义连接DBF数据库方法
private OdbcConnection OpenDbfDatabase(string databaseFileName)
{
string dbfFileName = databaseFileName;
//此处为连接DBF数据库的具体代码
return conn;
}
//调用方法
OdbcConnection conn = OpenDbfDatabase(dbfFilePathAndFileName); //打开DBF数据表并回传conn
其中dbfFilePathAndFileName为string类型变量,是从网页打开文件选择框中取得的DBF文件名(含路径和扩展名)。
3.1 创建MDB文件即创建Access数据库
.NET2.0框架下动态创建Access数据库需要在应用系统中添加引用“Microsoft ADD Ext.2.8 For DDL and Security”,如图 1所示:
图 1
然后通过使用ADOX命名空间下CatalogClass类的Create方法实现[1],实现的关键代码为:
ADOX.CatalogClass myCatalogClass = new ADOX.CatalogClass();
myCatalogClass.Create("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + mdbFileName + ";");
其中变量mdbFileName表示待创建的Access数据库文件名(含路径和扩展名)。
3.2 创建Access数据表
Access数据表与DBF数据库一样都是属于二维关系数据库,即以每个字段为一列、每行为一条记录的数据集合。然而它们的字段类型并不是一一对应关系,因此为了保证转换后数据的完整性必须先明白二者的字段类型的对应关系。表格 1是它们的字段类型对应表:
表格 1
DBF文件
|
MDB文件
|
|
字段类型
|
字段类型
|
对应System命名空间的字符表示
|
字符型
|
文本
|
System.String
|
货币型
|
数字
|
System.Decimal
|
数值型
|
数字
|
System.Decimal
|
浮动型
|
数字
|
System.Decimal
|
日期型
|
日期/时间
|
System.DateTime
|
日期时间型
|
日期/时间
|
System.DateTime
|
双精度型
|
数字
|
System.Double
|
整型
|
数字
|
System.Int32
|
逻辑型
|
是/否
|
System.Boolean
|
备注型
|
文本
|
System.String
|
通用型
|
二进制
|
System.Byte[]
|
字符型(二进制)
|
二进制
|
System.Byte[]
|
备注型(二进制)
|
二进制
|
System.Byte[]
|
在Access数据库中动态创建数据表的过程实际上就是创建数据库的结构,根据表格 1所提供的它们二者字段类型的对应关系,通过ADO.NET技术首先连接DBF文件,再利用ADO.NET Framework数据提供程序的DataReader对象即可取得DBF文件各字段属性(字段名称、类型等)。
.NET2.0框架下动态创建Access数据表也需要在应用系统中添加引用“Microsoft ADD Ext.2.8 For DDL and Security”,然后使用ADOX命名空间下TableClass类中的相关方法和属性实现[1]。实现的关键代码如下:
ADOX.TableClass tmpTable = new ADOX.TableClass();
tmpTable.ParentCatalog = myCatalogClass;
tmpTable.Name = "MyTable"; //待创建的数据表名
conn.Open(); //连接DBF数据库
OdbcCommand myDbfCommand = new OdbcCommand(sqlString, conn);
OdbcDataReader myDbfReader = myDbfCommand.ExecuteReader();
int DbfFieldCount = myDbfReader.FieldCount;
string myTableFieldName; //待创建的数据表字段名称
string myTableFieldType; //待创建的数据表字段类型
for (int i = 0; i < DbfFieldCount; i++)
{
ADOX.ColumnClass col = new ADOX.ColumnClass();
col.ParentCatalog = myCatalogClass;
col.Properties["Jet OLEDB:Allow Zero Length"].Value = false;
myTableFieldName = myDbfReader.GetName(i).ToString();
//获取数据表字段名称
col.Name = myTableFieldName;
myTableFieldType = myDbfReader.GetFieldType(i).ToString();
//获取数据表字段类型
switch (myTableFieldType)
{
case "System.String":
{
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adVarChar, 255);
break;
}
case "System.Decimal":
{
col.Type = ADOX.DataTypeEnum.adDouble;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adDouble, 20);
break;
}
case "System.DateTime":
{
col.Type = ADOX.DataTypeEnum.adDate;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adDate, 10);
break;
}
case "System.Double":
{
col.Type = ADOX.DataTypeEnum.adDouble;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adDouble, 20);
break;
}
case "System.Int32":
{
col.Type = ADOX.DataTypeEnum.adSingle;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adSingle, 10);
break;
}
case "System.Boolean":
{
col.Type = ADOX.DataTypeEnum.adBoolean;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adBoolean, 2);
break;
}
case "System.Byte[]":
{
col.Type = ADOX.DataTypeEnum.adBinary;
tmpTable.Columns.Append(col, ADOX.DataTypeEnum.adBinary, 255);
break;
}
default:
break; }
}
myCatalogClass.Tables.Append(tmpTable);
System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpTable);
System.Runtime.InteropServices.Marshal.ReleaseComObject(myCatalogClass);
tmpTable = null;
myCatalogClass = null;
conn.Close();
//GC.WaitForPendingFinalizers();
//GC.Collect();
3.3 给数据表以行为单位追加数据
这一步骤是整个转换工作的核心,实际上就是每次从DBF文件读出一行记录后将数据以行为单位插入到Access表中。那么是如何来完成数据的读出与插入呢?
3.3.1 .NET平台中数据源更新机制
ADO.NET是.NET框架的重要组成部分,它为应用程序提供数据访问的能力,如为运行能用于访问关系型数据库系统,为运行SQL查询、存储过程、操作XML数据提供所需的工具;DataSet和.NET Framework数据提供程序是组成ADO.NET的二个核心组件,DataSet是一组驻留于内存的数据集,它具有类似关系数据库的结构,主要由数据表(DataTable)和关系组成,.NET Framework数据提供程序是一组包含Connection、Command、DataReader、DataAdapter对象在内的组件,表格 2列出了这四个对象的主要功能:
表格 2
对象
|
说明
|
Connection
|
与指定的数据源建立连接
|
Command
|
对数据源执行命令操作
|
DataReader
|
从数据源中读取只进且只读的数据流
|
DataAdapter
|
用数据源填充DataSet并解析更新
|
从表中可以看出DataAdapter充当了数据源和DataSet的桥梁,它有二个作用:①从数据源将数据读入数据集DataSet②从数据集将已更改数据(插入记录、更新数据、删除记录)写回数据源。每一个DataAdapter对象都有四个属性,如表格 3下表:
表格 3
属性
|
说明
|
SelectCommand
|
引用SQL语句从数据存储区检索行
|
InsertCommand
|
引用命令向数据存储区插入行
|
UpdateCommand
|
引用命令修改数据存储区中的行
|
DeleteCommand
|
引用命令从数据存储区删除行
|
DataAdapter有二个常用方法:Fill方法和Update方法,Fill方法用于使用DataAdapter的SelectCommand的结果来填充DataSet;Update方法可以将DataSet中的数据更新回原数据源,调用Update方法时,DataAdapter将先分析已做出的更改(数据行DataRow数据发生变化),然后使用InsertCommand(插入操作)、UpdateCommand(更新操作)、DeleteCommand(删除操作)来处理该更动,所以在使用Update方法前必须显式设置这些命令。
.NET平台中利用ADO.NET技术将变化了的数据回写到数据库的相应数据表中的方法有二个:①通过创建一个Command对象执行特定的SQL语句来实现;②使用DataAdapter对象中的更新逻辑实现。本文采用的是第二种方法,在这四个属性中只有SelectCommand和InsertCommand属性与数据的插入有关,下面以具体的代码来说明转换的实现过程,代码已封装成一个方法。
3.3.2 给Access数据表追加数据完成DBF向MDB文件的转换的方法(关键代码)
//定义往Access数据表追加数据的方法,参数mdbFileName代表Access数据库文件名
private void AddDataToMdbTable(OdbcConnection conn,string mdbFileName)
{
string myTableFieldName;
string myTableFieldName2;
string myTableFieldType;
string fieldString1 = "";
string fieldString2 = "";
OleDbType tmpType = System.Data.OleDb.OleDbType.VarChar;
int myTableFieldSize = 100;
string connectionString = @"Provider = Microsoft.Jet.OLEDB.4.0;";
connectionString = connectionString + " Data Source=" + mdbFileName;
OleDbConnection myConnection = new OleDbConnection();
myConnection.ConnectionString = connectionString;
myConnection.Open(); //连接MDB数据库
string sqlMdbString = "select * from myTable";
OleDbCommand myCommand = new OleDbCommand(sqlMdbString, myConnection);
OleDbDataAdapter mdbDa = new OleDbDataAdapter(sqlMdbString, myConnection);
OleDbCommand myInsertCommand = new OleDbCommand();
myInsertCommand.Connection = myConnection;
conn.Open(); //连接待转换的DBF数据库
OdbcCommand myDbfCommand = new OdbcCommand(sqlString, conn);
OdbcDataReader myDbfReader = myDbfCommand.ExecuteReader();
int DbfFieldCount = myDbfReader.FieldCount;
for (int i = 0; i < DbfFieldCount; i++)
{
myTableFieldName = myDbfReader.GetName(i).ToString(); // 获取DBF文件字段名称
myTableFieldName2 = "@" + myTableFieldName; //形成与DBF文件相同的字段名称,字符‘@’不能少
fieldString1 += myTableFieldName + ",";
fieldString2 += "@" + myTableFieldName + ",";
myTableFieldType = myDbfReader.GetFieldType(i).ToString(); //获取字段类型
switch (myTableFieldType)
{
case "System.String":
{
tmpType = System.Data.OleDb.OleDbType.VarChar;
myTableFieldSize = 100;
break;
}
case "System.Decimal":
{
tmpType = System.Data.OleDb.OleDbType.Decimal;
myTableFieldSize = 10;
break;
}
case "System.DateTime":
{
tmpType = System.Data.OleDb.OleDbType.Date;
myTableFieldSize = 12;
break;
}
case "System.Double":
{
tmpType = System.Data.OleDb.OleDbType.Double;
myTableFieldSize = 8;
break;
}
case "System.Int32":
{
tmpType = System.Data.OleDb.OleDbType.Integer;
myTableFieldSize = 4;
break;
}
case "System.Boolean":
{
tmpType = System.Data.OleDb.OleDbType.Boolean;
myTableFieldSize = 1;
break;
}
}
myInsertCommand.Parameters.Add(new OleDbParameter(myTableFieldName2, tmpType, myTableFieldSize, myTableFieldName));
}
fieldString1 = "INSERT INTO myTable(" + fieldString1 + ")";
fieldString1 = fieldString1.Remove(fieldString1.LastIndexOf(","), 1);
fieldString2 = "VALUES(" + fieldString2 + ")";
fieldString2 = fieldString2.Remove(fieldString2.LastIndexOf(","), 1);
string fieldString = fieldString1 + " " + fieldString2;
myInsertCommand.CommandText = fieldString;
while (myDbfReader.Read())
{
OleDbDataAdapter myAdapter = new OleDbDataAdapter();
myAdapter.SelectCommand = myCommand;
myAdapter.InsertCommand = myInsertCommand;
DataSet newTableDs = new DataSet();
myAdapter.Fill(newTableDs);
DataTable newTable = newTableDs.Tables[0];
DataRow NewMyRow = newTable.NewRow();
for (int i = 0; i < DbfFieldCount; i++)
{
string tmpFieldName = myDbfReader.GetName(i).ToString();
tmpFieldName = tmpFieldName.Trim();
NewMyRow[tmpFieldName] = myDbfReader[i];
}
newTable.Rows.Add(NewMyRow);
myAdapter.Update(newTableDs);
newTableDs.Tables[0].AcceptChanges();
myAdapter.Dispose();
newTableDs.Clear();
newTableDs.Dispose();
}
myDbfReader.Close();
conn.Close();
myConnection.Close();
myConnection.Open();
string tmpString = "select * from myTable";
OleDbDataAdapter da = new OleDbDataAdapter(tmpString, myConnection);
DataSet mdbDs = new DataSet();
da.Fill(mdbDs);
GridView2.DataSource = mdbDs;
GridView2.DataBind();
myConnection.Close();
da.Dispose();
mdbDs.Clear();
mdbDs.Dispose();
// GC.WaitForPendingFinalizers();
// GC.Collect();
}