使用 Mimer Provider Manager 构建数据库无关的 ADO.NET 程序






4.39/5 (15投票s)
2004年4月1日
7分钟阅读

101378

2767
编写数据库无关的 ADO.NET 应用程序
引言
本文将向您展示如何编写不依赖于特定数据提供程序或数据库的应用程序。这是通过使用 Mimer Provider Manager 框架来实现的。
Mimer Provider Manager 简介
当您使用 ADO.NET 开发访问数据库的应用程序时,您会为不同的数据库使用不同的数据提供程序。因此,您无法编写通用的数据库无关应用程序。Mimer Provider Manager (Mpm) 系统可以轻松构建高效的应用程序,这些应用程序无需更改任何代码即可用于不同的数据库。Mimer Provider Manager 通过一个统一的接口来实现这一点,该接口封装了不同供应商特定的数据库接口。您可以将 Mpm 视为一个 ADO.NET 提供程序分发器,它使用不同的插件来访问不同的底层 ADO.NET 提供程序。从应用程序的角度来看,Mpm 只是另一个 ADO.NET 提供程序。目前,已为 Oracle、Mimer SQL、SQL Server、ODBC 和 OLEDB 开发了插件。还有一个通用插件,可以使用反射以稍高的成本访问任何底层提供程序。
除了插件之外,Mpm 中还有一个 SQL 过滤器概念。这允许您编写修改发送到数据库的 SQL 的过滤器。这样,您可以自动转换不同 SQL 方言之间的差异,并实现真正的数据库独立性。Mpm 的第一个版本包含一个用于处理不同参数标记的过滤器。由于使用了标准的 ADO.NET 命名约定,因此将现有应用程序转换为使用 Mpm 所需的工作量与将其转换为使用任何其他提供程序的工作量相同。Mimer Provider Manager Administrator 是一个 GUI,用于定义应用程序可用的数据源。由于您仅在应用程序中提供数据源名称,因此您可以轻松地切换数据库,而无需修改源代码。管理员还用于告诉系统您想使用哪些 SQL 过滤器以及加载新的提供程序插件。这意味着您也可以在不更改代码的情况下应用 SQL 过滤器。例如,您可以应用随附的 ParameterMarkers 过滤器,该过滤器可以处理应用程序中使用的不同参数标记。数据源定义可以存储在配置文件中并随应用程序一起分发,因此您无需在客户端进行任何配置。Mimer Provider Manager Administrator 还会为您处理繁琐的连接字符串。这通过属性网格完成,您可以在其中为每个提供程序选择所有可用选项。不同提供程序连接字符串语法之间的转换会自动为您完成。
Mpm 已集成到 Visual Studio .NET 中,因此您可以将 MpmCommands
、MpmConnections
等拖放到您的各种窗体上。文档也已集成,以便可以与 Microsoft 的标准文档一起阅读,包括对动态帮助的支持。还有一个已开发的机制,用于生成新的插件。这是通过 Visual Studio .NET 中的新项目向导完成的。因此,新插件将迅速添加。Mpm 支持并行执行的概念,以便多个版本可以在同一台计算机上并行安装。
数据浏览器示例
作为使用 Mpm 的工作示例,我们将开发一个基于 Windows 窗体的类数据浏览器。用户可以在两个组合框中选择所有可用的数据源及其相应的表。当用户选择了一个数据源和一张表并按下“加载”按钮后,将创建一个 DataSet
并显示在一个可更新的 DataGrid
中。如果行被更新并按下“加载”按钮,更改将被发送到数据库。ADO.NET 的架构允许您以连接到数据库或断开连接的方式进行工作。断开连接的模型使用 DataAdapter
,DataAdapter
会自动处理连接/断开连接。这是我们在本示例应用程序中所做的大部分工作。但是,我们将使用 MpmCommand
和 MpmDataReader
来获取数据库中可用的表。
您需要做的第一件事是在您的计算机上设置 Mpm 框架。
- 从 developer.mimer.com/mpm 或 www.sourceforge.net/projects/mimerpm 下载最新的二进制发行版。
- 启动 Windows 安装程序。这将在您指定的文件夹中安装 Mpm,将必需的 Mpm 程序集安装到全局程序集缓存中,并在可用时将 Mpm 与 Visual Studio .NET 集成。
- 启动 Mimer Provider Manager Administrator 并为您的数据库创建数据源。如果您愿意,也可以通过编程方式从您的应用程序创建数据源。
当您使用 Mpm 时,您可以使用 Mimer.Mpm.Data.Extensions
命名空间中的 MpmInfo
类来获取有关系统的运行时信息,例如已注册的数据源、插件和 SQL 过滤器。当数据浏览器启动时,它用于填充数据源的 ComboBox
。
comboDataSource.Items.AddRange(MpmInfo.DataSourceNames);
选择数据源后,表 ComboBox
将使用相应的表进行填充。这是通过一个普通的 SQL 语句来实现的,该语句查询标准化的 INFORMATION_SCHEMA 视图。首先调用 DataSourceChanged(...)
事件处理程序,其中创建了一个新的 MpmConnection
。然后调用 GetTables()
方法,该方法查询数据库并获取表。
private void DataSourceChanged(object sender, System.EventArgs e)
{
mpmConnection = new MpmConnection();
mpmConnection.ConnectionString = "Data Source Name=" +
comboDataSource.SelectedItem.ToString();
GetTables();
}
private void GetTables()
{
MpmCommand tableCommand = null;
MpmDataReader reader = null;
comboTables.Items.Clear();
try
{
//The connection should not be open when we get here
if(mpmConnection.State == ConnectionState.Open)
{
throw new Exception("The connection shouldn't be open here");
}
if(mpmConnection.DataSourceDescriptor.DbmsType == MpmDbmsTypes.Oracle)
{
throw new Exception("Oracle does not have INFORMATION_SCHEMA");
}
mpmConnection.Open();
tableCommand = mpmConnection.CreateCommand();
tableCommand.CommandText =
"select table_schema, table_name from " +
"information_schema.tables where table_type='BASE TABLE'";
reader = tableCommand.ExecuteReader();
comboTables.BeginUpdate();
while(reader.Read())
{
comboTables.Items.Add(reader.GetString(0) + "." +
reader.GetString(1));
}
comboTables.EndUpdate();
}
catch(Exception ex)
{
errorHandler.ShowException(this, ex);
}
finally
{
try
{
if(reader != null)
{
reader.Close();
}
}
catch(Exception ex)
{
errorHandler.ShowException(this, ex);
}
try
{
if(mpmConnection.State != ConnectionState.Closed)
{
mpmConnection.Close();
}
}
catch(Exception ex)
{
errorHandler.ShowException(this, ex);
}
}
}
名为 errorHandler
的类用于处理可能的异常,我们将在本文后面展示它。如上所示,与使用任何其他提供程序唯一的区别在于 Mpm 命名约定和简化的连接字符串语法。当用户按下“加载”按钮时,会调用一个名为 LoadDataSet(string tableName)
的方法。该表名用于构造一个 select 命令,该命令由 MpmDataAdapter
使用以创建 DataSet
,我们将其绑定到我们的 DataGrid
。这就是显示几乎任何数据库中任何表的内容的全部内容。
private void LoadDataSet( string tableName)
{
try
{
StringBuilder selCmd = new StringBuilder();
selCmd.Append("select * from ");
selCmd.Append(tableName);
mpmDataAdapter = new MpmDataAdapter(selCmd.ToString(),
mpmConnection);
ds = new DataSet();
mpmDataAdapter.Fill(ds, tableName);
dataGrid.CaptionText = tableName;
dataGrid.DataSource = ds.Tables[tableName].DefaultView;
}
catch(Exception ex)
{
errorHandler.ShowException(this, ex);
}
}
处理更新的方法同样简单。当用户编辑了一些列并按下 GUI 中的“更新”按钮时,将调用该方法。将创建一个包含所有已更改行的新的 DataSet
。如果新的 DataSet
包含任何行,则将 MpmCommandBuilder
附加到 MpmDataAdapter
,并在 MpmDataAdapter
上调用 Update 方法。
private void UpdateDataSet()
{
try
{
DataSet ds2 = ds.GetChanges();
if(ds2 != null)
{
MpmCommandBuilder mBuild = new MpmCommandBuilder(mpmDataAdapter);
mpmDataAdapter.Update(ds2, ds.Tables[0].TableName);
ds.AcceptChanges();
}
}
catch(Exception ex)
{
errorHandler.ShowException(this, ex);
}
}
错误和警告
在所有示例中,我们都使用一个名为 ErrorHandler
的类来处理我们的异常。在使用 Mpm 操作数据库时,您可以捕获 MpmException
并使用 MpmError
类来获取更多信息。
catch(MpmException me)
{
StringBuilder msg = new StringBuilde();
foreach(MpmError mErr in me.Errors)
{
ExtractErrors(mErr, msg);
}
MessageBox.Show(msg.ToString(),
"Caught a MpmException",
MessageBoxButtons.OK);
}
ExtractErrors(mErr, msg)
是一个助手方法,可用于错误和警告。
private void ExtractErrors(MpmError mErr, StringBuilder msg)
{
if(mErr.Message.Length > 0)
{
msg.Append("\r\nError message: ");
msg.Append(mErr.Message);
}
if(mErr.SQLState.Length > 0)
{
msg.Append("\r\nSQLState: ");
msg.Append(mErr.SQLState);
}
if(mErr.NativeError != 0)
{
msg.Append("\r\nNative error: ");
msg.Append(mErr.NativeError);
}
if(mErr.Source.Length > 0)
{
msg.Append("\r\nSource: ");
msg.Append(mErr.Source);
}
}
在我们的示例应用程序中,我们将上述功能提取出来,并将其放在一个名为 ErrorHandler
的辅助类中。
要在 ADO.NET 中以及因此在 Mpm 中接收警告,您必须编写一个事件处理程序并将其注册到要接收事件的对象上。我们可以为此编写以下方法。
public void HandleWarnings(object sender, MpmInfoMessageEventArgs e)
{
StringBuilder msg = new StringBuilder();
foreach(MpmError mErr in e.Errors)
{
ExtractErrors(mErr, msg);
}
MessageBox.Show(msg.ToString(),
"Received a MpmInfoMessageEvent",MessageBoxButtons.OK);
}
要为连接注册事件处理程序,只需将其添加到 MpmConnection
上的 InfoMessage
属性。
mpmConnection.InfoMessage += new MpmInfoMessageEventHandler(HandleWarnings);
在这些示例中,我们仅处理 MpmExceptions
。根据您的操作,您可能需要处理不同类型的异常。
原生方法
在某些情况下,您可能希望访问特定于提供程序的特性,Mpm 并不阻止这样做。相反,Mpm 提供了让您与原生提供程序协同工作的方法,并且可以轻松地在代码中识别。例如,如果您想在 Sql Server 中使用事务保存点,您可以这样做。
MpmConnect connect = new MpmConnect("Data Source Name=SqlSource");
MpmTransaction transaction = connect.BeginTransaction();
//Do some database calls
MpmDataSourceDescriptor dataSource = connect.DataSourceDescriptor;
if (dataSource.DbmsType == MpmDbmsTypes.SqlServer) {
// SQLServer specific actions
SqlTransaction sqltransaction =
(SqlTransaction) transaction.NativeTransaction;
sqltransaction.Save("SavepointName");
}
正如您所看到的,我们使用运行时信息来查找要使用的原生提供程序。然后,我们将 MpmTransaction
强制转换为 SqlTransaction
并调用 Save
方法。
摘要
正如您所看到的,使用 Mimer Provider Manager 编程数据库逻辑的方式与直接使用特定数据提供程序的方式没有区别。事实上,Mimer Provider Manager 可以看作是另一个数据提供程序,只不过它适用于任何有数据提供程序的数据库。就像使用其他数据提供程序连接数据库时一样,您使用连接字符串来标识要使用的数据库。使用 Mpm 时,连接字符串代表一个逻辑名称,该名称可以指向任何类型的数据库并使用任何类型的数据提供程序。结合更高级的功能,如 SQL 过滤器,您可以编写真正数据库无关的应用程序。
有关更多信息和下载,请访问 Mpm 网站 developer.mimer.com/mpm 或 Sourceforge 项目 www.sourceforge.net/projects/mimerpm。