一 引言
早在年我就着手开发一个ASPNET在线消息板应用程序WebForumsNET其目的是创建一个基于ASPNET的消息板系统而且该系统可以容易插入到一个现有网站中构建这样一个端对端应用程序的特别挑战之一就是要为客户提供一种方式以便能够把它集成到他们自己的系统中去例如一个在线论坛明显需要使用某种数据存储来存储用户信息论坛回寄信息等但是最好不要把客户锁定到一种特定的数据存储中也就是说你不应该说我的应用程序必须使用微软的SQL Server 因为这样的话使用Oracle或Access的客户怎么会使用你的软件呢?
另一个集成到客户现有数据中的问题是所有的在线论坛站点都提供用户帐户和一种创建新帐户的方式典型情况下这被建模为一个论坛架构(以一个数据库中的Users表形式存在)但是客户很可能已经有他们自己的拥有成千的用户帐户的数据库表或者一个客户可能想在一个内部网设置中使用该论坛并且想使用活动目录而不是某种数据库表来认证和存储用户信息因此当一个论坛软件系统创建一个Users数据库表并对其客户说这就是你存储用户的方式时那些已经拥有现有基础结构和用户数据的客户很可能会疏远这样的软件
因此当你使用一种僵硬的API构建一个系统时会产生特别的挑战一种僵硬的API不是提供一种方式来定制逻辑而是硬编码实现细节(例如你必须使用SQL Server作为你的后端数据存储且在这个数据库有一个Users表并将在其中存储所有的用户信息)然而通过使用提供者设计模式你可以轻易地打破这种僵硬性借助于提供者设计模式系统架构师只需要定义API至于编程功能则由系统来提供对于一个在线论坛应用程序来说这可能包括一个具有例如Authenticate(usernamepassword)和GetUserDetails(username)等方法的Users类
提供者模型的优秀在于客户实现方案可以指定一个系统应该使用的定制类这种定制类必须实现系统的良好定义的API但是它允许无缝地插入任何定制实现也就是说一旦定义这个API系统实现者可以创建一个使用SQL Server和一个Users表的默认的具体实现大多数客户可以直接使用之而不必要作任何修改那些有定制需要的客户(他们想使用Oracle或以另外一些方式存储用户数据)可以创建他们自己的类该类提供必要的功能并且把它们插入到这些客户的系统中
其实提供者设计模式被应用于整个ASPNET 实现中当然网上也存在一些如何在ASPNET x应用程序中使用这一功能的教程
在本文中我们将详细探讨提供者模型并分析如何把它应用于ASPNET 开发中
二 打破僵硬的API实现
在我早期的WebForumsNET开发中我认识到这种僵硬的API实现将会成为一个问题我的软件设计目标之一就是尽可能灵活且可定制并且使用户使用SQL Server而且我的用户数据模型实现应该看起来充其量只是有些限制性为了克服这些问题我构建了一个包含下面两部分的系统
一组定义了系统的核心功能的抽象基类
能够在运行时刻动态地加载一个扩展抽象基类具体地说该代码负责检查包含一个<ConfigSetting>节(该节中给出要使用的类的完全限定名)的nfig文件
借助于这一架构我可以通过一系列抽象基类来定义系统的功能并使用SQL Server 和Users表来提供这些类的具体实现满足这一配置的客户可以只管使用该应用程序并且一切将工作良好且不需要他们编写一行代码然而那些需要定制的开发者们可以通过创建他们自己的派生自适当的抽象基类的类来实现通过简单地把该程序集放到应用程序的/bin目录并更新nfig文件他们可以让系统使用这个新类具体地说WebForumsNET发行中带有一个抽象基类DataProvider它清楚地列举出了系统中的所有方法类似如下
public abstract class DataProvider
{
public abstract bool AuthenticateUser(string usernamestring password);
public abstract User GetUserInfo(string username);
public static DataProvider Instance()
{
}
}
AuthenticateUser(usernamepassword)和GetUserInfo(username)方法是系统定义的许多方法中的两个方法的代表而静态Instance()方法是该DataProvider类的主要实现它包含检查代表了WebForumsNET配置信息(该信息指示系统要使用的类的全称限定名)的nfig文件的代码然后该方法使用反射(和缓沖)来创建该类的一个实例并且把它返回到系统
WebForumsNET发行中还带有一个派生自DataProvider基类的SqlDataProvider类这个类提供分类方法的具体实现例如SqlDataProvider的所有方法都可以操作存储于一个SQL Server 数据库中的数据与用户相关的方法可以与一个预定义的Users数据库表一起工作一个想改变后端功能的客户可以创建他自己的派生自DataProvider的类这些信息都可以展示于nfig文件中(指明应该使用他们的定制类)例如WebForumsNET中的nfig可能包括下列内容
<WebForumsSettings>
<add key=DataProviderAssemblyPath value=path />
<add key=DataProviderClassName value=NamespaceClassname />
</WebForumsSettings>
默认情况下这个设置信息引用随同WebForumsNET一起发行的SqlDataProvider类然而如果一个客户创建他自己的API实现那么他可以提供自己的类的细节并且系统会自动地开始使用他的实现来创建默认实现
借助于这一架构使用WebForumsNET的页面开发者可以使用如下所示的代码来认证一个用户
if (DataProviderInstance()AuthenticateUser(usernamepassword))
//用户被认证
else
//用户名/口令无效!
当调用DataProviderInstance()方法时上面的配置文件被检查并且返回适当类的一个实例如果客户还没有创建他们自己的实现的话这将是默认的SqlDataProvider类而如果他们已经实现的话它将是他们自己的类一旦DataProviderInstance()方法返回一个适当的提供者实例我们就可以简单地调用该API的成员(在这个例子中是AuthenticateUser())
WebForumsNET提供者模型一个早期的原型
相对于微软建议使用的提供者模型Andy的提供者模型含有一些不足一方面WebForumsNET中提供了单个抽象基类所有的API定义都聚集在这个类中其负面作用在于如果一个客户仅想定制系统的一小部分例如用户信息的存储方式那么他必须提供该系统中所有方法的实现一种更好的方案是为系统中的每一个逻辑实体创建一个抽象基类例如对于一个在线消息板应用程序来说它可能需要一些类如UsersProviderForumsProviderPostsProvider等等然而在你提供给一个客户的提供者数目之间也存在一个平衡问题更多提供者允许更为细致的系统定制但是也会相应地提高要求的配置标记的数量
另外我已经展示了对WebForumsNET的提供者模型实现代码的作了进一步改进以便使其更相似于微软在ASPNET 中所使用的代码我认为Andy的想法应该是提供者模型的先驱尽管微软的提供者模型实现更为清晰且更强壮一些
一方面WebForumsNET在年三月为微软所收购另一方面Rob Howard及其他人又在系统中加入了大量的新特征并且在ASPNET论坛中以自由方式发行它Today Rob及其小组成员已经把ASPNET论坛变成了一个Community Server(它简直把博客论坛画廊列表服务器新闻阅读器等全部融为一体)今天Andy所创建的概念与实现被广泛应用于ASPNET论坛和Community Server中甚至被应用于许多核心ASPNET 组件中
三 提供者模型优点
提供者模型提供许多优点首先在代码和后端实现之间存在清晰的分离不管认证一个用户的代码是针对一个SQL Server 数据库的Users表还是针对于一个活动目录存储从页面开发者的观察看来代码都是相同的
DataProviderInstance()AuthenticateUser(usernamepassword)
而且后端实现变化是透明的
因为系统架构师被鼓励创建默认的实现所以提供者模型提供了两种世界的最好结合如果对默认实现已经比较满意那么系统会按预期进行工作对于需要定制系统的用户来说他们尽管定制好了而不必干扰现有代码或编程逻辑这个设计模式也使得原型化和灵敏开发容易许多例如在早期系统使用阶段仅使用默认实现可能更容易然而以后你可能需要定制某些方面以便把该工作与你的公司的现有系统集成到一起这时你可以通过提供者模型实现需要的定制这意味着不需要改变你的早期工作来反映后端实现的变化
就象许多好的设计模型一样提供者模型也提供了开发者之间的职责分离这样以来一部分开发者可以使用他们精通的系统API进行工作而另一部分开发者可以专注于后端实现和定制任务开发而且这两组人员可以工作在同一个系统上而不会相互干扰而且如果他们所使用的系统是一种工业标准(例如ASPNET )那么这两类任务中的技能都可以被容易地移植到未来的工作中
四 ASPNET 提供者模型
ASPNET 在全部其架构中都利用提供者模型例如它的许多子系统会员站点导航个性化等都利用了提供者模型技术而且每个子系统都提供一种默认实现但也能使客户定制其功能以满足他们自己的需要例如ASPNET 的站点导航部分允许一个页面开发者定义他们网站的可导航的结构然后这些数据就可以被各种Web控件所使用以便显示站点地图树状视图或菜单它们能够高亮站点的导航并且/或者显示用户的站点位置除了与导航相关的Web控件外站点导航API还提供了一组方法用于实现与网站导航信息的交互
默认地站点的可导航信息必须以一个正确格式化的XML文件编码这种数据存储方式是默认站点导航被硬编码使用然而ASPNET 的提供者模型可以使你更容易地使用你自己的数据存储来实现站点导航例如在一个我当前开发的工程中使用的一个数据库包含站点中的页面信息以及不同的用户拥有页面中什么样的许可权不是在一个XML文件中重新定义这些信息并且必须努力保持两份信息的及时更新而是通过利用ASPNET 中的站点导航功能我可以简单地创建一个提供者类它能够直接与数据库信息工作一旦创建这个类并且在网站的配置中指定导航Web控件就可以根据存储在数据库中的应用程序的定制导航信息进行工作(注意在本文成文之时这个工程使用的仍然是ASPNET x然而这个例子却有希望能够向你展示提供者模型的优点)
作为个人我想提供者模型只是ASPNET 提供的最优秀的迁移特征之一ASPNET 提供了很多开发者在x版本中必须定制的新特征如果这些版本的新特征使用过于僵硬的实现方式那么它将阻止基于定制方案的正在使用中的x应用程序向它的迁移因为许多新的ASPNET Web控件都使用了这些新的子系统然而有了提供者模型后我们就可以把我们的x应用程序升级到版本并且创建一个提供者以便使版本的新的子系统与我们的定制方案集成到一起这意味着当迁移到版本时我们可以使用新的Web控件并且使它们通过提供者模型而自然地使用我们的现有系统
五 补充信息
随着提供者模型成为ASPNET 中的一个重要组成部分微软出版了很多关于这个主题的文章如果你想了解更多这方面的信息那么我鼓励你阅读一下Rob Howard的两篇文章
· 《提供者模型设计模式与规范》
· 《ASPNET x提供者模型》
其中第二篇文章分析了如何把提供者模型应用于你的ASPNET x应用程序还有两篇文章讨论了ASPNET 的站点导航子系统中的提供者用法
· 《理解和扩展ASPNET 中的站点导航系统》作者David Gristwood
· 《定制ASPNET 中的提供者》作者Morgan Skinner
注意微软还发行了另外一个提供者开发工具包它也用于创建ASPNET 提供者而且还有一篇不错的文章《ASPNET 提供者模型》可供你参考
六 结论
当创建具有各种要求的大量客户所使用的系统时一种僵硬的API实现可能会吓坏了开发者这种僵硬的实现往往会强迫客户同意且被锁定于系统架构师的视野之内而一般地公司往往更对能够与他们的现有方案协同工作的应用程序和框架感兴趣而不是强迫他们的方案服从供应商提供的系统
提供者模型提供了一种打破这种僵硬的实现问题的方法借助于提供者模型系统就能够灵活地使用扩展特定基类的任何类这样以来客户可以创建他们自己的包括他们的定制逻辑和业务规则的派生类而且这些新类可以无缝地插接到系统中而不必干扰应用程序中的现有代码或任何自创建以来的新的定制代码
总之提供者模型在ASPNET 中得到普通使用而且这些概念也可以应用于ASPNET x应用程序中