DNN 数据访问
虽然在.NET Framework中可以使用多种数据访问方式,且这些方式各有优缺点,但是DNN只选择能在80%的案例中使用的方式作为默认的数据访问方式,要使用别的访问方式,需要单独开发。
DNN的数据访问是建立在原生数据库这一特性的先进性上(比如由.net管理驱动,私有SQL方言,存储过程等等)。为了实现访问多个不同类型的数据库并实现代码重用,DNN提出了数据访问层这个概念。即建立特殊的抽象数据访问类,并把它作为驱动样例。在实际使用中,一个方法的抽象类被创建,每一个方法必须被一个数据访问类声明。对每一格我们想支持的数据库,我们创建一个实体类,并写入特殊的代码来表现每一个在抽象类的操作。为了支持在运行时调用需要的实体类,我们也包括一个instance()方法,依靠我们通用的驱动类去读取配置文件。为了节约反应时间,DNN在缓存中保存数据驱动类的构造函数。
为什么不使用接口来替代抽象类呢?因为接口不支持继承,对类的图样设计不能应用到接口,在接口上加一个方法与在基类中加一个抽象方法是等价的,一些实现接口的类将中断,因为这些类不能实现新的方法。
这种解决方案的优点是数据库访问类只要实现数据驱动抽象类的方法后就能在业务逻辑类后被编译。这意味着如果我们想创建别的数据库的实现,不用改变业务逻辑层。创建另一个实现的方法:
1、 为新数据库创建一个实现数据驱动抽象类方法的数据库访问类
2、 编译这个类为一个组件
3、 测试并部署这个新的数据组件到一个运行的服务器
4、 改变配置文件以指向这个新的数据库访问类
5、 在业务逻辑层部分不需要改变或重新编译
Web.config文件包含许多重要的部分来启动数据驱动图样。第一部分注册驱动和他们的反应配置部分句柄,实现的示例代码如下:
<configSections>
<sectionGroup name="dotnetnuke">
<section name="data" type="DotNetNuke.ProviderConfigurationHandler, DotNetNuke" />
</sectionGroup>
</configSections>
需要注意的是section name的值需要在web.config文件中的别的地方实现。下面部分是保留的依靠旧的方法的数据访问:
<appSettings>
<add key="connectionString" value="Server=localhost;Database=DotNetNuke;uid=sa;pwd=;" />
</appSettings>
在<dotnetnuke>群中的<data>标签需要包含一个默认的驱动属性和后面在<providers>中的conllection关联。这个默认的驱动比用来作为单个的改变选择在一个驱动到另一个驱动间。如果默认的驱动没有指定,那么在collection中的第一项被认为是默认的。在<data>节中,每一个驱动必须包括一个名字,类型,和驱动路径属性(名称是通用的但通常是指类名,类型指驱动的完整类名,驱动路径是指在本地能被脚本发现的驱动特殊资源)。每个驱动还包括一些自定义的属性如下:
<dotnetnuke>
<data defaultProvider="SqlDataProvider" >
<providers>
<clear/>
<add name = "SqlDataProvider"
type = "DotNetNuke.Data.SqlDataProvider, DotNetNuke.SqlDataProvider"
connectionString = "Server=localhost;Database=DotNetNuke;uid=sa;pwd=;"
providerPath = "~"Providers"DataProvider"SqlDataProvider""
objectQualifier = "DotNetNuke"
databaseOwner = "dbo"
/>
<add name = "AccessDataProvider"
type = "DotNetNuke.Data.AccessDataProvider, DotNetNuke.AccessDataProvider"
connectionString = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"
providerPath = "~" Providers"DataProvider"AccessDataProvider""
objectQualifier = "DotNetNuke"
databaseFilename = "DotNetNuke.resources"
/>
</providers>
</data>
</dotnetnuke>
可以通过<add><remove><clear>来对<providers>中的驱动进行相关操作,需要注意的是<clear>清除其面所有的驱动。以下是相关的两个文件:
"Components"Provider.vb
这个文件包含所有从web.config文件中加在驱动信息及实现操作的所有细节,它是一个通用的类,而不仅仅是用于数据访问。
"Components"DataProvider.vb
这个文件是一个抽象类,它包含所有DNN的数据访问方法。它包含一个instance()方法,工厂化本身,根据web.config文件中的配置加载必要的组件。
所有数据访问方法都被定义为必须重定义,这意味着所有从这个类继承的数据驱动必须提供这些方法的实现。这是抽象类在业务逻辑层和数据访问层之间的定义。
数据访问层
数据访问层必须实现在DataProvider抽象类中的方法。当然每一个数据访问层驱动可以用不同的代码来实现这些方法。这将允许驱动可以灵活选择自己的数据库访问协议(managed.NET,OleDB,ODBC等等)。他也允许驱动处理数据库平台间的不同私有属性(比如:stored procedures,SQL language syntax,@@IDENTITY)。
每一个数据驱动必须定义一个在web.config文件指定的对他们自定义属性的实现。数据访问方法必须被设计为单个的查询以便他们能在所有数据库中实现(比如单个的SELECT,INSERT,UPDATE,DELETE)。这就意味着像条件判断,计算或则本地变量将在业务逻辑层实现。
数据库脚本
DNN包含自动更新特性,允许应用程序自动更新数据库,脚本必须被命名为版本号加数据驱动名(比如:02.00.00.SqlDataProvider),必须位于在web.config文件中providerPath属性指定的目录中。动态的替代可以在脚本中实现,通过在驱动中实现重载ExecuteScript方法。因为许多数据库没有丰富的脚本语言,每个驱动也有必要实现UpgradeDatabaseSchema方法,这用来使程序能改变数据库结构。
SQL语法
SQL server和MSDE示例:
drop procedure {databaseOwner}{objectQualifier}GetPortalTabModules
go
create procedure {databaseOwner}{objectQualifier}GetPortalTabModules
@PortalId int,
@TabId int
as
select {objectQualifier}Modules.*,
{objectQualifier}Tabs.AuthorizedRoles,
{objectQualifier}ModuleControls.ControlSrc,
{objectQualifier}ModuleControls.ControlType,
{objectQualifier}ModuleControls.ControlTitle,
{objectQualifier}DesktopModules.*
from {objectQualifier}Modules
inner join {objectQualifier}Tabs on {objectQualifier}Modules.TabId = {objectQualifier}Tabs.TabId
inner join {objectQualifier}ModuleDefinitions on {objectQualifier}Modules.ModuleDefId = {objectQualifier}ModuleDefinitions.ModuleDefId
inner join {objectQualifier}ModuleControls on {objectQualifier}ModuleDefinitions.ModuleDefId = {objectQualifier}ModuleControls.ModuleDefId
inner join {objectQualifier}DesktopModules on {objectQualifier}ModuleDefinitions.DesktopModuleId = {objectQualifier}DesktopModules.DesktopModuleId
where ({objectQualifier}Modules.TabId = @TabId or ({objectQualifier}Modules.AllTabs = 1 and {objectQualifier}Tabs.PortalId = @PortalId))
and ControlKey is null
order by ModuleOrder
GO
MS Access示例:
drop procedure {objectQualifier}GetPortalTabModules
go
create procedure {objectQualifier}GetPortalTabModules ( [@PortalId] int, [@TabId] int )
as
select {objectQualifier}Modules.*,
{objectQualifier}Tabs.AuthorizedRoles,
{objectQualifier}ModuleControls.ControlSrc,
{objectQualifier}ModuleControls.ControlType,
{objectQualifier}ModuleControls.ControlTitle,
{objectQualifier}DesktopModules.*
from {objectQualifier}Modules, {objectQualifier}Tabs, {objectQualifier}DesktopModules, {objectQualifier}ModuleDefinitions, {objectQualifier}ModuleControls
where {objectQualifier}Modules.TabId = {objectQualifier}Tabs.TabId
and {objectQualifier}Modules.ModuleDefId = {objectQualifier}ModuleDefinitions.ModuleDefId
and {objectQualifier}ModuleDefinitions.ModuleDefId = {objectQualifier}ModuleControls.ModuleDefId
and {objectQualifier}DesktopModules.DesktopModuleId = {objectQualifier}ModuleDefinitions.DesktopModuleId
and ({objectQualifier}Modules.TabId = [@TabId] or ({objectQualifier}Modules.AllTabs = True and {objectQualifier}Tabs.PortalId = [@PortalId]))
and isnull(ControlKey) = True
order by ModuleOrder;
GO
注意:由于MDB文件能被下载,可以将其改为资源文件(.resource)。
数据库对象命名
在web.config文件中包含一个名为objectQualifer的属性,这允许你为你的数据库队形指定前缀(比如:、DNN_)。这样一个是避免在与其他程序共用数据库时名称重复,另外这些共同前缀名的文件在数据管理系统中能在一起显示。
应用程序段
Microsoft Data Access Application Block是.NET的组件,它提供优化的数据访问代码来帮你调用存储过程(.NET专用),或者将SQL server数据库转化为SQL文本语言。DNN使用它作为建造块来节省大量自定义代码。DNN同时提供一个Ole.ApplicationBlocks.Data组件来提供基于MSDAAB代码的Access数据驱动。在实现方面,DNN基本采用了这个组件来替代在DAL(数据访问层)实现的人工代码。
数据传输
DNN使用DataReader实现数据访问层到业务逻辑层的数据传输,之所以用它是因为他是最快的。
业务逻辑层
好的项目要求业务逻辑层和数据库分开,DNN的业务逻辑层被有效的定义在"Components子文件夹中。业务逻辑层包含一些表现层用来调用的多种应用服务的抽象类。在数据访问方面,业务逻辑层向前API调用相关的数据驱动,使用前面提到的DataProvider工场。
自定义业务对象是来源于在用户指定的结构中使用数据包裹技术的对象。自定义业务对象要求一些在定义的代码,像清算这样的安全编程模式,无法连接数据存储和初始化的情况下。自定义业务逻辑对象提供最大的弹性,当他们允许程序在他们自己的抽象对象中定义数据结构;消除对自由数据容器的依赖。
什么是安全编程模式?请想象如下数据访问代码的例子:variable = DataReader(“fieldname”)如果数据库中的值不是被定义为variable,那么在编译时不会出错,但是在运行时汇报错。如果我们使用自定义业务对象访问数据代码:variable = Object.Property 在这种情况下编译的时候编译器就会告诉我们数据类型不匹配。同时这种方式更聪明也提高代码可读性。
一组对象我们称它聚集或者集合。DNN使用标准的数组列表来描述一组自定义业务对象。数组列表是ASP.NET固有的对象包含所有你想要的集合的特性(add,remove,find,iterate),更重要的是他被允许与一些.NET控件进行绑定。
为什么不直接将数据从数据访问层传送给自定义业务对象,而要经过业务逻辑层呢?因为DNN认为将数据从业务逻辑独立有优势。比如,在一些项目中属性只用在表现层而不必存入数据库。
自定义业务对象助手
经过最小化自定义业务对象从数据层(经过DataReader)获取信息的平常编码工作的努力后,一个通用的工具类被创建欻里,这个类包含两个公用函数,一个是用于单一对象,一个用于集群对象。对这个类的每个属性的常规假设定义在DataReader中有一个一致的文件。要想自动对应信息必须在名称和类型上一致。下面是掩饰代码:(略)
NULL操作
差不多所有数据库都有一个结构来指定没有被定义的数据的初始值,在大多数关系型数据库中这个结构被设为值NULL。在DNN中表现层和数据层传递NULL值是一个结构性的挑战。因为表现层必须从数据库细节中抽象出来,因而它必须能被定义,当一个属性的值没有被设定的时候。这个事情将来更复杂,实际上.NET Framework 天生的数据类型中没有从数据库中表示NULL值得能力。每个数据库中标志NULl值的实现不同,因此这个问题的最好解决方案是在应用程序的各个层之间创建一个抽象的传输服务,它能被用来编码/反编码NULL值。DNN官方为此提供了如下代码(略)
缓存
有一个缓存操作类,可以参考。
开发
DNN提供一个灵活的门户软件框架,应用程序核心提供一般功能比如:成员管理、角色安全,个性化,管理,站点登陆,导航和数据访问的管件服务。他也提供对特殊商业用途的扩展能力。在许多案例中,在特殊的商业功能被从核心代码中抽象,通过自定义模块来实现是被推荐的。这保证核心代码的纯洁,提供最好的升级选择。当然,你也可以修改核心代码。下面是一个自定义模块的例子。