最近在公司做一个小项目,其中一部分功能涉及到要在程序中获得数据库的架构(元数据)信息,所谓数据库的架构,就是数据库的表/视图结构,包括数据表所包含的字段名、字段类型、长度、主键、外键信息等。记得曾经也用C++Builder工具实现过相关功能,但年代久远,源码也没有保存,实现方法已经完全回忆不起来了。想想反正开发工具也换成了vs,倒不如重头再来过。
要取得数据库架构信息倒不是一件难度很高的事,在网络上以"数据库元数据"为关键字进行搜索,会找到很多有关方法的介绍,但多半是与数据库系统相关的方法,例如介绍在SQL Server下哪些数据表存储了有关元数据的信息,可以通过哪些数据库系统提供的函数来获取等,这里也提供两个有关的链接,有兴趣的可以自行去阅读相关文章:
http://searchdatabase.techtarget.com.cn/tips/433/3388433.shtml(如何获取SQL Server数据库元数据)
http://www.pipcn.com/blog/user1/master/archives/2006/1119.shtml(获取SQL,Access,Oracle数据库的元数据信息)
但是我总想着能用更为通用的方法、较少的程序代码来完成这项工作。说实话,实现了之后我发现,如果我能够安心下来对ADO.NET进行系统的学习,其实这个问题很容易解决,但我想找现成的实现,反而耽误了一些时间。总之是在网络上搜索没能找到现成的代码,我不得不回头再来看ADO.NET的有关文档。这下倒是让我很快就找到了Connection对象的GetSchema方法获得数据表和视图的信息,以及Command对象的ExecuteReader方法通过带CommandBehavior.SchemaOnly参数提取表结构,这样,实现代码非常简单:
/// <summary>
/// 获取数据库的表架构信息
/// </summary>
/// <returns>返回当前数据源的架构信息列表</returns>
public void GetSchema(out List<SqlSchemaInfo> lst)
{
lst = new List<SqlSchemaInfo>();
//GetSchema方法所需的字符串参数,Tables和Views分别表示数据表和视图
string tables = System.Data.SqlClient.SqlClientMetaDataCollectionNames.Tables;
string views = System.Data.SqlClient.SqlClientMetaDataCollectionNames.Views;
//这里创建的Connection对象仅作为示例,只实现对sqlserver和oracle的支持
if (_providerName.Trim() == "sqlserver")
{
_Connection = new System.Data.SqlClient.SqlConnection(_connectionString);
}
else if (_providerName.Trim() == "oracle")
{
_Connection = new System.Data.OracleClient.OracleConnection(_connectionString);
}
try
{
if (_Connection.State == ConnectionState.Closed)
{
_Connection.Open();
}
//调用GetSchema取数据源的表架构
DataTable tbl = _Connection.GetSchema(tables);
foreach (DataRow row in tbl.Rows)
{
SqlSchemaInfo ssi = new SqlSchemaInfo();
if (_providerName.Trim() == "oracle")
{
ssi.Owner = (string)row["OWNER"];
}
ssi.Type = "Table";
ssi.Name = (string)row["table_name"];
lst.Add(ssi);
}
//调用GetSchema取数据源的视图架构
tbl = _Connection.GetSchema(views);
if(tbl.Rows.Count > 0)
{
foreach (DataRow row in tbl.Rows)
{
SqlSchemaInfo ssi = new SqlSchemaInfo();
ssi.Type = "VIEW";
if (_providerName.Trim() == "oracle")
{
ssi.Name = (string)row["VIEW_NAME"];
ssi.Owner = (string)row["OWNER"];
}
else
{
ssi.Name = (string)row["table_name"];
}
lst.Add(ssi);
}
}
}
catch (Exception ex)
{
throw (ex);
}
}
以上方法需要特别说明的是在提取Oracle数据表架构时,要保存“OWNER”字段的值,这样才能在下面根据数据表名称或视图名称提取表结构时不会发生异常,另外,Oracle的视图名称保存在“VIEW_NAME”字段中,而SQL Server的视图名称保存在“table_name”字段。
下面的程序代码根据数据表名或视图名获得指定表/视图的结构,表名/视图名就是通过上面的方法获得的。如果是Oracle数据库系统,传递的表名称(tableName)参数应该是OWNER + "." + TableName或OWNER + "." + ViewName的形式:
/// <summary>
/// 根据表名称获取数据表结构
/// </summary>
/// <param name="tableName">数据表/视图名称</param>
/// <param name="cols">返回表结构的列表</param>
public void GetSqlColumns(string tableName, out List<SqlColumn> cols)
{
IDbCommand cmSQL = null;
IDataReader dr = null;
cols = new List<SqlColumn>();
try
{
// Open up a connection
if (_Connection == null)
{
return;
}
if (_Connection.State == ConnectionState.Closed)
{
_Connection.Open();
}
cmSQL = _Connection.CreateCommand();
cmSQL.CommandText = "SELECT * FROM " + tableName;
dr = cmSQL.ExecuteReader(CommandBehavior.SchemaOnly);
for (int i = 0; i < dr.FieldCount; i++)
{
SqlColumn sc = new SqlColumn();
sc.Name = dr.GetName(i);
sc.DataType = dr.GetFieldType(i);
cols.Add(sc);
}
}
catch (Exception sqle)
{
throw (sqle);
}
finally
{
_Connection.Close();
if (cmSQL != null)
{
cmSQL.Dispose();
if (dr != null)
dr.Close();
}
}
}
这两个方法已经通过了SQL Server和Oracle数据库系统的测试,通过扩展还能够支持OLEDB和ODBC数据源。