65.9K
CodeProject 正在变化。 阅读更多。
Home

XQuiSoft数据使用Provider模式(开源)概述

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.59/5 (12投票s)

2006 年 4 月 27 日

BSD

8分钟阅读

viewsIcon

42067

downloadIcon

92

一个适用于 .NET 应用程序的数据抽象层。让您的应用程序代码与数据库无关。在不更新甚至不重新编译业务组件的情况下,切换应用程序使用的数据库类型。

引言

XQuiSoft Data 是一个用于数据库无关的 .NET 应用程序的开源组件。它是 XQuiSoft 应用程序框架的一部分,该框架完全开源。源代码下载包含演示业务组件和演示网站。本文中的所有源代码都可以在下载中找到。您可以在 此处 获取该项目的最新版本。

此数据组件依赖于 XQuiSoft 框架中的另一个开源项目,即 XQuiSoft Provider 组件。它基本上是 1.1 框架中用于提供程序模式的实现,类似于 2.0 框架中的实现。此开源项目可以在 此处 找到。

在本文中,我将展示如何从应用程序的业务层使用 XQuiSoft Data 组件的示例。可下载的源代码将包含一个调用这些组件的示例 Web 项目,我将只对其进行简要讨论。

演示设置

首先,请确保您下载了正确的版本。本文发布时的最新版本是 2.2.50727.0。早期版本展示了组件的一种不同用法,该用法将来可能会过时。未来版本也将包含演示项目。要获取所需的 xqs-provider 引用,您可以下载包含已编译形式的 XQuiSoft Data 二进制文件,或者从 xqs-provider 下载区域下载源代码。

XQuiSoft.Data 的源代码下载包含三个项目。第一个是 Data 类库源代码,它编译为XQuiSoft.Data.dll。第二个是一个演示库,其中包含本文中大部分代码片段,它编译为XQuiSoft.Data.Demo.dll。第三个项目是 Web 应用程序项目,展示了以下示例代码的用法。

  1. 在这些项目上方的文件夹中创建一个新的解决方案文件,并将每个项目添加到其中。我有一个包含其他核心 xquisoft 项目的不同解决方案,因此我无法将其包含进来。
  2. 更新 Web 应用程序的web.config。您必须将数据库连接字符串更新为拥有创建表访问权限的数据库。安装程序需要此访问权限。如果您愿意自行运行脚本,那么连接字符串中的所有用户只需要具有运行脚本创建的存储过程的访问权限。
  3. 现在解决方案应该可以正常运行/调试。第一次运行时,将启动页设置为Install.aspx。然后导航到EmployeeList.aspx页面进行编辑记录。

此演示的目标是演示如何设计您的业务层代码。用户界面的外观极简。

基本功能

易用性:您无需担心打开连接、创建命令或释放数据读取器等资源。您只需要知道要调用哪个命令,并将适当的参数值传递给命令。

易于学习:提供的类上的方法名称与 .NET 基础框架中的方法名称对应。这应该使您能够轻松地将现有代码升级到使用此组件。

可扩展性:数据组件基于提供程序模式。这意味着 DataManager 类将实际的写入调用委托给一个或多个配置的提供程序(实现),这些提供程序派生自基类 DataProvider 。每个提供程序定义了它交互的数据库类型以及如何执行数据库命令。目前内置了 SqlDataProvider OracleDataProvider

应用程序设计

配置设置

您需要做的第一件事是添加数据组件所需的某些配置。第一部分告诉框架从哪里加载将处理下一个自定义配置节的类。您的应用程序配置中只能有一个 'configSections' 标记。因此,如果您有其他具有自定义配置节的组件,请将它们与此合并。

<configSections>
    <sectionGroup name="XQuiSoft.Data">
        <section name= "DataManager" type="XQuiSoft.Data.DataManagerConfiguration, 
		XQuiSoft.Data" />
    </sectionGroup>
</configSections> 

您接下来需要的是定义所有数据库连接的配置节,这在 'configSections' 中已经描述过。

    <XQuiSoft.Data>
        <DataManager defaultProvider="Production">
            <providers>
                <!-- The provider instance to use on localhost development -->
                <add name="Development"
                    type="XQuiSoft.Data.SqlClient.SqlDataProvider, XQuiSoft.Data"
                    connectionString="Password=pw;Persist Security Info=True;
			User ID=DATABASEUSER
;Initial Catalog=DATABASENAME;Data Source=SERVERNAME\INSTANCENAME" />
                <!-- The provider instance to use when deployed to production -->
                <add name="Production"
                  type="XQuiSoft.Data.SqlClient.SqlDataProvider, XQuiSoft.Data"
                  connectionString="Password=pw;Persist Security Info=True;
		User ID=DATABASEUSER;
		Initial Catalog=DATABASENAME;Data Source=SERVERNAME\INSTANCENAME" />
            </providers>
        </DataManager>
    </XQuiSoft.Data> 

providers 节点包含每个要配置的数据库连接的子节点。type 属性是代表您希望连接的数据库类型的数据库实现提供程序。对于内置类型,您实际上不需要完整的程序集名称。在这种情况下,“XQuiSoft.Data.SqlClient.SqlDataProvider”就足够了。如果未指定程序集,提供程序组件将在执行程序集中搜索指定的类型。connectionString 属性对于您使用的数据库类型必须有效。

请注意,DataManager 节点上可以有两个属性。您可以有一个 'defaultProvider',它等于子节点之一的名称,或者您可以有一个 'defaultProviderAppSettingName',它等于 app settings 节点中的一个设置名称。该设置的值必须等于子节点中的提供程序名称之一。

    <appSettings file="Environment.config">
        <add key="ENVCODE" value="Development" />
    </appSettings> 

这样做的原因是,您可以让您的web.config在所有部署环境中保持一致。然后,每个环境都可以有一个单独的Environment.config文件,其中有一个“ENVCODE”值。该文件应与上面的 appSettings 节点看起来相同,只是没有指定 file 属性。

基本实体

在我们的示例应用程序中,我们将只查看和编辑员工。这只是为了保持简单。因此,我们首先需要定义我们的自定义实体类 'Employee',以及一个派生自 CollectionBase 的强类型集合 'EmployeeCollection'。这在下载中提供,并且这些类没有什么特别之处。我不会在这里解释它们。

使用 DataManager 的具体提供程序实现

从一个 static 服务类开始,该类定义了您的应用程序所需的各种方法。在我们的例子中,我们需要获取、保存和删除员工。接下来定义一个具有相同签名的 abstract 方法的基提供程序类。然后我们需要一个名为 DbEmployeeProviderEmployeeProvider 的具体实现类。下面是该类的轮廓,并展示了一些方法实现以供讨论。要查看 EmployeeProvider 基类或 EmployeeManager 服务类,请参阅下载中的代码。

public class DbEmployeeProvider: EmployeeProvider
{
	#region Fields
	/*...*/
	#endregion Fields

	#region Constructors
	/*...*/
	#endregion Constructors

	#region Properties
	/*...*/
	#endregion Properties

	#region Methods
	public override void Initialize(string name, 
			NameValueCollection configValue)/*...*/
	public override ExecutionResults Install()/*...*/
	/*...*/
	public override Employee GetEmployee(int id)
		{
		Procedure proc = new Procedure("Demo_GetEmployee");
		proc.AddInputParameter("EmployeeID", DbType.Int32, id);
		
		DataFactoryDelegate builder = 
			new DataFactoryDelegate(this.AddEmployee);
		EmployeeCollection col = new EmployeeCollection();
		dataProvider_.Execute(proc, builder, col);

		if (col.Count > 0)
			return col[0];
		else
			return null;
	}
	/*...*/
	public override EmployeeCollection GetSubOrdinates(int managerID)/*...*/
	/*...*/
	public override bool SaveEmployee(Employee item)
	{
		Procedure proc = new Procedure("Demo_SaveEmployee");
		proc.AddParameter("EmployeeID", 
			DbType.Int32, ParameterDirection.InputOutput, item.ID);
		proc.AddInputParameter("ManagerID", DbType.Int32, item.ManagerID);
		proc.AddInputParameter("FirstName", DbType.AnsiString, item.FirstName);
		proc.AddInputParameter("LastName", DbType.AnsiString, item.LastName);

		int rows = dataProvider_.ExecuteNonQuery(proc);
		Parameter prmID = proc.Parameters.Find("EmployeeID");
		item.ID = dataProvider_.GetInt32(prmID.Value);
		return (rows > 0);
	}
	/*...*/
	public override bool DeleteEmployee(int id)/*...*/
	/*...*/
	protected virtual void AddEmployee(IList target, IDataRecord record)
	{
		Employee item = new Employee();
		item.ID = dataProvider_.GetInt32(record, "EmployeeID", -1);
		item.ManagerID = dataProvider_.GetInt32(record, "ManagerID", -1);
		item.FirstName = dataProvider_.GetString(record, "FirstName");
		item.LastName = dataProvider_.GetString(record, "LastName");
		target.Add(item);
	}
	#endregion Methods
}

可以通过实例化 Procedure 类并传入所需的名称、添加参数值,然后调用适当的 Execute 方法来调用存储过程。请注意,过程参数名称不应包含特定于数据库提供程序的前缀。数据提供程序将处理添加该前缀(如果适用),然后再将参数传递给 ADO.NET。默认的 SqlDataManager 提供程序类会向 ADO.NET 参数添加一个 @ 前缀(如果尚未存在)。

GetEmployee 方法执行过程,然后另一个方法 AddEmployee 被传递给 Execute 方法的 DataFactoryDelegate 参数。此委托方法将为数据读取器从数据库连接返回的每个记录调用。此方法实例化一个新数据对象实例(在本例中为 Employee 类),填充属性,并将新项添加到提供的集合中。

SaveEmployee 方法也使用过程,但它会插入或更新记录(并且不返回任何记录)。它调用 ExecuteNonQuery 并将填充了参数的 Procedure 实例传递进去。过程上定义的输出参数用于在 ExecuteNonQuery 调用完成后填充记录的 ID。

提供程序模型配置

为了使用这个 EmployeeManager 服务,您需要将其配置为使用 DbEmployeeProvider ,如下所示。请注意,您需要将此新的 sectionGroup 的内容合并到用于配置 XQuiSoft.Data 的同一个 configSections 节点中。请注意,下面的连接名称未指定。当未指定 connectionName 时,DbEmployeeProvider 使用配置中 DataManager 部分定义的默认 DataProvider

<configSections>
    <sectionGroup name="XQuiSoft.Data.Demo">
        <section name= "EmployeeManager" 
		type="XQuiSoft.Data.Demo.EmployeeManagerConfiguration" />
    </sectionGroup> 
</configSections>
<XQuiSoft.Data.Demo>
    <EmployeeManager>
        <providers>
            <add name="Default" 
	    type="XQuiSoft.Data.Demo.Data.DbEmployeeProvider, XQuiSoft.Data.Demo" />
        </providers>
    </EmployeeManager>
</XQuiSoft.Data.Demo>

如果您的数据库是共享的,以至于记录分布在多个数据库中,您可能希望指定 DbEmployeeProvider 要使用的每个连接,或者可以使用默认连接在主数据库连接上执行查询以确定哪个连接包含给定查询的数据。数据库共享设计超出了本文的范围。但是,DataManager 确实支持在需要数据库共享的场景下提供简便的编程体验。

应用程序用法(UI)

从用户界面使用服务类非常简单。首先,我们需要一个可以绑定到结果的表单。由于 EmployeeCollection 派生自 CollectionBase,它实现了 IList ,这正是 DataGrid 设计用于处理的内容(不仅仅是数据集)。

EmployeeCollection emps = EmployeeManager.GetSubordinates(mgrId);
DataGrid1.DataSource = emps;
DataGrid1.DataBind();

就是这样!您现在可以将 DbEmployeeProvider 替换为不同的实现,而用户界面代码无需更改,甚至无需重新编译!

结论

我介绍了 XQuiSoft Data 组件的基本功能和用法。其中一些功能实际上是从 XQuiSoft Provider 组件继承的,所以请务必也查看一下。我很乐意听取您使用这些组件的成果。我没有涵盖演示项目中所有可见的代码。其中一些代码用于支持提供程序的自动安装功能。我可能会在未来的文章中讨论此功能。对于像此演示这样的简单应用程序,它并不是真正必需的,但在插件架构中非常有用。

如果您开始一个新项目,请考虑 XQuiSoft Data 的优势。对其进行评估,看看它是否适合您的工具箱。无论哪种情况,我都希望听到您对评估的反馈。

历史

  • 2006 年 4 月 27 日:文章初稿提交
  • 2006 年 4 月 28 日:添加了额外的下载说明
  • 2009 年 6 月 16 日:更新以使用 XQuiSoft.Data 在过去一年半中引入的最新功能。这次更新早就该进行了。
© . All rights reserved.