日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

DNN 数据访问策略 (转)

發布時間:2023/11/27 生活经验 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DNN 数据访问策略 (转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

經過幾天斷斷續續的努力,這篇文章終于翻譯結束,文章主要講了DNN的數據訪問策略,對于了解系統整體上是如何工作的有一定的幫助,希望能給dnn的初學者一些有用的信息。由于翻譯的匆忙+水平有限,錯誤或不當之處在所難免,歡迎大家討論、指正。

原作者:

Shaun Walker – Perpetual Motion Interactive Systems Inc.

http://www.perpetualmotion.ca

?

?

目錄

?

?

簡介... 2

策略... 2

需求... 3

配置... 4

數據訪問層 ( DAL ). 8

數據庫腳本... 11

數據庫對象命名... 12

應用程序塊... 12

數據傳輸... 12

業務邏輯層 ( BLL ). 13

自定義業務對象助手 ( CBO ). 14

空處理機制... 16

實現細節... 19

緩存... 25

性能... 26

開發... 26

自定義模塊... 27

改進核心模塊... 29

sql命令發生器... 30

參考... 30

?

?

?

?

?

?

?

簡介

?

DotNetNuke(以下簡稱DNN)的最終目的是創建一個門戶的框架平臺,這個平臺可以為開發者增添模塊搭建應用程序提供堅實的可靠的支持。應用程序的一個關鍵的功能就是數據存取。.NET Framework提供了多種數據存取的方法,從架構的角度來看從這么多方法中選出適合自己的需求的最佳的解決方案很難。本白皮書將嘗試著在DNN應用程序的實現中提供最合適的數據存取策略。

策略

?

?

在很多資料中有各種關于.NET Framework數據存取方法的介紹,然而他們大多脫離了現實的實際應用。雖然大家常常討論的是這些方法的優點和缺點,但是現在仍有很多開發者不知道如何選擇自己的最佳策略。事實上每種方法都有適合它的不同的用例,理論上這是對的,這也是難以選擇的原因,然而在實踐中,每一個開發者都在尋找一個適合所有企業應用的存取策略。

一致的數據存取策略有許多好處,有了統一定義好的數據存取策略,開發者就無需浪費時間來為每個任務選擇數據存取方法,這種模式提高了代碼的維護性,在所有的應用程序范圍內實現了一致性。通過數據存取組件的集中處理使得數據存取策略風險降低了,也使得代碼的完整性增強了。

一致的兼容的數據存取策略的概念的確跟每個需求應用其最佳的數據存取策略相悖。為每個應用程序選擇相應的最佳的數據存取策略能夠獲得最好的性能(假定你能夠從所有的用例中篩選出最適合的方案)。可是這樣可能導致團隊在開發實踐中難以協調的合作。

DotNetNuke拋棄了眾所周知的傳統的80/20原則,它把精力集中在提供一致的兼容的數據存取策略,這個策略理想的目標是把80%的精力放在應用程序用例上,剩下的20%用來考慮跟用其他的數據存取方法相比是否性能要求是必須的,同時也采用了上面所述的策略。

?

?

?

?

需求

?

DNN的一個重要的需求就是要提供一個能夠支持多種數據存儲應用程序的實現方法。

????? 由于對外部數據存儲通信的靈活性和性能的要求,我們選擇放棄一般的數據存取方法而打造一個新的應用,這個新的應用主要利用了數據庫本地化特征集(也就是用.NET管理提供者、所有的SQL語言、存儲過程等等)。在選擇特殊的數據庫訪問類時我們做了權衡,我們需要為我們想要支持的每個數據庫平臺寫一個特殊的數據訪問層,因此應用程序也就包含了更多的代碼。數據訪問層共享了大量共同的代碼,每一個訪問層都明確的處理了特殊數據庫應用。

????? 為了簡便的應用數據庫訪問類我們選擇了提供者模式(也就是GOF描述的工廠設計模式),這種模式是通過反射的在應用程序運行時動態的加載正確的(適合的)數據訪問對象。工廠是這樣實現的:先創建一個抽象類,這個類聲明了一個方法,這個方法是每一個數據訪問類都必須繼承實現的。對每一個我們支持的數據庫,我們創建了一個具體的實現類,這個類實現了抽象類或“協議”中定義的各種數據庫操作的代碼。為了支持在運行時動態加載具體的操作類,我們在工廠里實現了一個Instance()方法,這個方法依賴于提供者類從配置文件讀取并反射過來的值來決定需要加載哪個程序集。由于反射在應用程序性能方面非常耗費資源,我們把數據庫提供者類的構造器儲存到緩存里。

????? ?那么為什么用抽象類而不用接口呢?這是因為接口是(不可變的)靜態的,而且因此接口也不能將其自身復制(翻譯)。由于接口不支持實現方法繼續繼承,所以這些類的模式不采用接口。為接口增加一個方法跟為基類增加一個抽象方式是等效的;任何類實現了接口它也就終止了,因為這些類不能再實現新的方法(確切的說應該是接口定義以外的方法接口無法使用)。

????? 下面的圖表展示了業務邏輯、工廠以及數據庫存取類是如何相互聯系的。這個解決方案的關鍵優勢是只要數據訪問類實現了DataProvider抽象類的方法,數據庫訪問類就能夠在業務邏輯類之后編譯。這就意味著在我們想要創建另一個數據庫的實現方法時我們無需改變業務邏輯層(或用戶界面層),創建另一個實現方法的步驟是:

1、? 為新的數據庫創建數據庫訪問類,這些類實現了DataProvider的抽象類。

2、? 將這些類類編譯成一個程序集。

3、? 測試并配置這個新的數據訪問程序集到正在運行的服務器。

4、? 修改配置文件,來指定新的數據庫訪問類。

5、? 無需對業務邏輯組件進行任何改變也無需重新編譯它。

?

?

?

?

?

配置

?

?????? Web.config文件包含了許多配置節來使DataProvider模式起作用。第一個配置節注冊了這些提供者(Providers)和他們相應的配置節處理方法(ConfigurationSectionHandlers)。盡管在這個例子中我們僅僅展示了DotNetNuke配置節組中的一個,我們可以通過類似的方法配置其它的提供者(也就是抽象驗證提供者等等)。但有一點是必須保證的,那就是web.config文件中必須實現這些配置節。

?

??? <configSections>

??????? <sectionGroup name="dotnetnuke">

??????????? <section name="data" type="DotNetNuke.ProviderConfigurationHandler, DotNetNuke" />

??????? </sectionGroup>

??? </configSections>

?

下面的配置節是為老式的數據訪問方法的模塊而保留的:

??? <appSettings>

??????? <add key="connectionString" value="Server=localhost;Database=DotNetNuke;uid=sa;pwd=;" />

??? </appSettings>

?

如上最終實現了大量的提供者模塊。<dotnetnuke>配置組中的<data>配置節名稱需要有一個默認的提供者(defaultProvider)屬性,這個屬性依賴于下面的<providers>集合中的特定的實例。在從一個provider轉變為另一個provider的時候defaultProvider被用來作為單一的轉換器。如果沒有指定默認的provider,這個配置集合中的第一項就被認為是默認的。

?????? <data>配置節也包含了一個<providers>集合說明,確定了所有的<data>實現都是唯一的。每一個provider都必須至少包含name、type和providerPath屬性(name不是特定的但是通常相應的類名,type指定了provider相關的強類名,providerPath指定了provider的特殊資源例如腳本在哪里)。每一個提供者同樣可以有多個自定義屬性。

?

?

??? <dotnetnuke>

??????? <data defaultProvider="SqlDataProvider" >

??????????? <providers>

??????? ? <clear/>

??????????????? <add name = "SqlDataProvider"

??????????????????????? type = "DotNetNuke.Data.SqlDataProvider, DotNetNuke.SqlDataProvider"

??????????????????????? connectionString = "Server=localhost;Database=DotNetNuke;uid=sa;pwd=;"

??? ??????????????????? providerPath = "~\database\SqlDataProvider\"

??????????????????????? objectQualifier = "DotNetNuke"

??????????????????????? databaseOwner = "dbo"

??????????????? />

??????????????? <add name = "AccessDataProvider"

??????????????????????? type = "DotNetNuke.Data.AccessDataProvider, DotNetNuke.AccessDataProvider"

??????????????????????? connectionString = "PROVIDER=Microsoft.Jet.OLEDB.4.0;"

??? ??????????????????? providerPath = "~\database\AccessDataProvider\"

??????????????????????? objectQualifier = "DotNetNuke"

??????????????????????? databaseFilename = "DotNetNuke.mdb"

??????????????? />

??????????? </providers>

??????? </data>

??? </dotnetnuke>

?

下面是對providers集合中的節點作用的詳細說明。

<providers>配置節包含了一個或多個<add>、<remove>、<clear>元素。下面的規則應用在處理這些元素的時候:

?

?

1、? 聲明一個空的<providers />不是錯誤。

2、? Providers繼承了父配置中<add>聲明的項。

3、? 如果某項已經存在了或被繼承了再用<add>重新定義那么這是錯誤的。

4、? <remove>一個不存在的項是錯誤的。

5、? 如果一個項被<add>后又被<remove>了,然后再<add>這個完全相同的項是可以的(不是錯誤)。

6、? 如果一個項<add>, <clear>,然后再<add>是可以的(不是錯誤的)。

7、? <clear>會清除所有在先前定義的和繼承的項。例如:先用<add>聲明再用<clear>清除那么項就不存在了,而在<clear>后再<add>聲明的項是不會被清除的。

?

<add>

描述

增加一個數據提供者(data provider)。

屬性

Name——provider的友好的名稱。

Type——一個實現了provider接口的類。這個值是一個程序集的完整的關聯。

providerPath——查找provider的特殊資源(如腳本)的路徑。

其它name/value對——也許還有一些附加的名稱/值對,所有的名稱/值對都是provider能夠理解的(處理的)。

?

<remove>

描述

清除一個指定的數據提供者

屬性

Name——要清除的provider的友好名稱。

?

?

<clear>

描述

清除所有的繼承的提供者。

?

\Components\Provider.vb

?

Provider.vb類提供了所有的實現細節,這些實現包括從web.config文件加載provider的信息以及應用<add>, <remove>, <clear>處理的規則(標準)。這是一個通用的(generic)類,它不僅僅適用于數據訪問。

?

\Components\DataProvider.vb

?

DataProvider.vb是一個抽象類,這個類包含了DNN的所有的數據訪問方法。它包含了一個工廠本身的實例方法(Instance()),它負責在運行時動態加載web.config中描述的合適的程序集。

?

??????? ' unique provider name used for caching

??????? Private Const ProviderType As String = "data"

??????? Private Const ProviderName As String = ""

?

??????? Public Shared Function Instance() As DataProvider

?

??????????? ' Use the cache because the reflection used later is expensive

??????????? Dim cache As System.Web.Caching.Cache = System.Web.HttpContext.Current.Cache

?

??????????? If cache(ProviderName & ProviderType & "provider") Is Nothing Then

?

??????????????? ' Get the name of the provider

??????????????? Dim objProviderConfiguration As ProviderConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType)

?

??????????????? ' The assembly should be in \bin or GAC, so we simply need to get an instance of the type

??????????????? Try

?

??????????????????? ' Get the typename of the DataProvider ( ie. DotNetNuke.Data.SqlDataProvider, DotNetNuke.SqlDataProvider )

??????????????????? Dim strTypeName As String = CType(objProviderConfiguration.Providers(objProviderConfiguration.DefaultProvider), Provider).Type

??????????????????? ' Override the typename if a ProviderName is specified ( this allows the application to load a different DataProvider assembly for custom modules )

??????????????????? strTypeName.Replace(objProviderConfiguration.DefaultProvider, ProviderName & objProviderConfiguration.DefaultProvider)

??? ????????????????' Use reflection to store the constructor of the class that implements DataProvider

??????????????????? Dim t As Type = Type.GetType(strTypeName, True)

?

??????????????????? ' Insert the type into the cache

??????????????????? cache.Insert(ProviderName & ProviderType & "provider", t.GetConstructor(System.Type.EmptyTypes))

?

??????????????? Catch e As Exception

?

??????????????? End Try

??????????? End If

?

??????????? Return CType(CType(cache(ProviderName & ProviderType & "provider"), ConstructorInfo).Invoke(Nothing), DataProvider)

?

??????? End Function

?

?

?????? 所有的數據訪問方法都被定義成必須重寫的(MustOverride)。也就是說所有的從詞類派生的數據提供者類都必須提供這些方法的實現。這些定義了業務邏輯層和數據存取層之間聯系的抽象類協議。

?

??????? ' links module

??????? Public MustOverride Function GetLinks(ByVal ModuleId As Integer) As IDataReader

??????? Public MustOverride Function GetLink(ByVal ItemID As Integer, ByVal ModuleId As Integer) As IDataReader

??????? Public MustOverride Sub DeleteLink(ByVal ItemID As Integer)

??????? Public MustOverride Sub AddLink(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

??????? Public MustOverride Sub UpdateLink(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

數據訪問層( DAL )

?

數據訪問層(DAL)必須實現數據提供者抽象類中聲明的方法。然而,每一個DAL提供者在實現這些方法時也許很不相同。這種處理允許提供者靈活的選擇他們自己的數據庫訪問協議(也就是說:.NET管理的OleDB, ODBC等)。同樣也允許提供者處理數據庫平臺之間的所有不同之處(例如:存儲過程,sql語言語法,@@IDENTITY)。

每一個數據提供者必須為其在web.config中的自定義屬性指定一個實現方法。

Imports System

Imports System.Data

Imports System.Data.SqlClient

Imports Microsoft.ApplicationBlocks.Data

Imports System.IO

Imports System.Web

Imports DotNetNuke

?

Namespace DotNetNuke.Data

?

??? Public Class SqlDataProvider

?

??????? Inherits DataProvider

?

??????? Private Const ProviderType As String = "data"

?

??????? Private _providerConfiguration As ProviderConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType)

??????? Private _connectionString As String

??????? Private _providerPath As String

??????? Private _objectQualifier As String

??????? Private _databaseOwner As String

?

??????? Public Sub New()

?

??????????? ' Read the configuration specific information for this provider

??????????? Dim objProvider As Provider = CType(_providerConfiguration.Providers(_providerConfiguration.DefaultProvider), Provider)

?

??????????? ' Read the attributes for this provider

??????????? _connectionString = objProvider.Attributes("connectionString")

?

??????????? _providerPath = objProvider.Attributes("providerPath")

?

??????????? _objectQualifier = objProvider.Attributes("objectQualifier")

??????????? If _objectQualifier <> "" And _objectQualifier.EndsWith("_") = False Then

??????? ????????_objectQualifier += "_"

??????????? End If

?

??????????? _databaseOwner = objProvider.Attributes("databaseOwner")

??????????? If _databaseOwner <> "" And _databaseOwner.EndsWith(".") = False Then

??????????????? _databaseOwner += "."

??????????? End If

?

??????? End Sub

?

??????? Public ReadOnly Property ConnectionString() As String

??????????? Get

??????????????? Return _connectionString

??????????? End Get

??????? End Property

?

??????? Public ReadOnly Property ProviderPath() As String

??????????? Get

??????????????? Return _providerPath

??????????? End Get

??????? End Property

?

??????? Public ReadOnly Property ObjectQualifier() As String

??????????? Get

??????????????? Return _objectQualifier

??????????? End Get

??????? End Property

?

??????? Public ReadOnly Property DatabaseOwner() As String

??????????? Get

??????????????? Return _databaseOwner

??????????? End Get

??????? End Property

?

數據訪問方法必須涉及成簡單的查詢(例如 單一的select,insert,update,delete),以便于在所有的數據庫平臺上他們都能被實現。業務邏輯(例如條件分支,計算或局部變量)應該在業務邏輯層實現,這樣才能從數據庫抽象出來并集中到一個應用程序(模塊)里處理。如果你經常用那些使你可以在數據庫層實現程序邏輯的富sql語言變量工作的話,這種數據庫訪問是相當簡單的。

DNN中的Sql server/msde 數據提供者用了存儲過程作為最好的數據訪問技術。

?

??????? ' links module

??????? Public Overrides Function GetLinks(ByVal ModuleId As Integer) As IDataReader

??????????? Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetLinks", ModuleId), IDataReader)

??????? End Function

??????? Public Overrides Function GetLink(ByVal ItemId As Integer, ByVal ModuleId As Integer) As IDataReader

??????????? Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetLink", ItemId, ModuleId), IDataReader)

??????? End Function

??????? Public Overrides Sub DeleteLink(ByVal ItemId As Integer)

??????????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "DeleteLink", ItemId)

??????? End Sub

??????? Public Overrides Sub AddLink(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

??????????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "AddLink", ModuleId, UserName, Title, Url, MobileUrl, IIf(ViewOrder <> "", ViewOrder, DBNull.Value), Description, NewWindow)

??????? End Sub

??????? Public Overrides Sub UpdateLink(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

??????????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "UpdateLink", ItemId, UserName, Title, Url, MobileUrl, IIf(ViewOrder <> "", ViewOrder, DBNull.Value), Description, NewWindow)

??????? End Sub

?

?????? DNN用到存儲過程(存儲過程查詢)但是沒有用參數自動查找的特性( CommandBuilder.DeriveParameters ),因此參數必須明確的定義。

?

??????? ' links module

??????? Public Overrides Function GetLinks(ByVal ModuleId As Integer) As IDataReader

??????????? Return CType(OleDBHelper.ExecuteReader(ConnectionString, CommandType.StoredProcedure, ObjectQualifier & "GetLinks", _

??????????????? New OleDbParameter("@ModuleId", ModuleId)), IDataReader)

??????? End Function

??????? Public Overrides Function GetLink(ByVal ItemId As Integer, ByVal ModuleId As Integer) As IDataReader

??????????? Return CType(OleDBHelper.ExecuteReader(ConnectionString, CommandType.StoredProcedure, ObjectQualifier & "GetLink", _

??????????????? New OleDbParameter("@ItemId", ItemId), _

??????????????? New OleDbParameter("@ModuleId", ModuleId)), IDataReader)

??????? End Function

??????? Public Overrides Sub DeleteLink(ByVal ItemId As Integer)

??????????? OleDBHelper.ExecuteNonQuery(ConnectionString, CommandType.StoredProcedure, ObjectQualifier & "DeleteLink", _

??????????????? New OleDbParameter("@ItemId", ItemId))

??????? End Sub

??????? Public Overrides Sub AddLink(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

??????????? OleDBHelper.ExecuteNonQuery(ConnectionString, CommandType.StoredProcedure, ObjectQualifier & "AddLink", _

??????????????? New OleDbParameter("@ModuleId", ModuleId), _

??????????????? New OleDbParameter("@UserName", UserName), _

??????????????? New OleDbParameter("@Title", Title), _

??????????????? New OleDbParameter("@Url", Url), _

???????????? ???New OleDbParameter("@MobileUrl", MobileUrl), _

??????????????? New OleDbParameter("@ViewOrder", IIf(ViewOrder <> "", ViewOrder, DBNull.Value)), _

??????????????? New OleDbParameter("@Description", Description), _

??????????????? New OleDbParameter("@NewWindow", NewWindow))

??????? End Sub

??????? Public Overrides Sub UpdateLink(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal Url As String, ByVal MobileUrl As String, ByVal ViewOrder As String, ByVal Description As String, ByVal NewWindow As Boolean)

??????????? OleDBHelper.ExecuteNonQuery(ConnectionString, CommandType.StoredProcedure, ObjectQualifier & "UpdateLink", _

??????????????? New OleDbParameter("@ItemId", ItemId), _

??????????????? New OleDbParameter("@UserName", UserName), _

??????????????? New OleDbParameter("@Title", Title), _

??????????????? New OleDbParameter("@Url", Url), _

??????????????? New OleDbParameter("@MobileUrl", MobileUrl), _

??????????????? New OleDbParameter("@ViewOrder", IIf(ViewOrder <> "", ViewOrder, DBNull.Value)), _

??????????????? New OleDbParameter("@Description", Description), _

??????????????? New OleDbParameter("@NewWindow", NewWindow))

??????? End Sub

?

數據庫腳本

?

?????? DNN包含了一個自動升級的特性,也就是當有了一個新的應用程序版本發布后,應用程序能夠自動更新數據庫。不過腳本必須根據版本號和數據提供者命名(例如02.00.00.SqlDataProvider),并且必須放在web.config文件中providerPath屬性所指定的目錄下。動態升級的實現需要腳本中重寫provider所實現的ExecuteScript的方法。這對安全規范和對象命名很有用。

create procedure {databaseOwner}{objectQualifier}GetLinks

?

@ModuleId int

?

as

?

select ItemId,

?????? CreatedByUser,

?????? CreatedDate,

??? ???Title,

?????? Url,

?????? ViewOrder,

?????? Description,

?????? NewWindow

from {objectQualifier}Links

where? ModuleId = @ModuleId

order by ViewOrder, Title

?

GO

?

數據庫對象命名??

?????? web.config文件包含了一個名字叫objectQualifer的屬性,它允許你為數據庫對象指定一個前綴(例如DNN_)。Web主機往往只提供一個數據庫服務器,因此必須跟其它web應用程序共享一個賬號。如果你沒有指定前綴也許會跟已經存在的其它應用程序的數據庫對象名稱沖突。指定前綴的另一個好處就是這些數據庫對象在SQL Server企業管理器等管理工具里當按字母排序的時候他們會分組顯示,這就方便了管理。

?????? 如果你升級的是DotNetNuke2.0以前版本的數據庫,你需要設置objectQualifier為“”。這是因為你也許用了第三方模塊,而這些模塊不用新的DAL體系架構而是用特定的對象名稱。升級設置objectQualifier時會將你所有的核心的數據庫對象重命名,這可能會使你的自定義的模塊出錯。

?

應用程序塊

?

?????? 微軟數據訪問應用程序塊(MSDAAB)是一個.NET組件,它包含了最優化的數據訪問代碼,它能幫助你依靠SQL Server數據庫調用存儲過程、發布sql文本命令。我們借用這些方法作為dnn的一個建造模塊,來減少創建、測試和維護大量自定義的數據訪問方法的代碼。我們也為基于MSDAAB代碼的微軟Access數據提供者創建了一個OleDB.ApplicationBlocks.Data程序集。

?????? 比起在我們的DAL實現方法里包含真正的資源代碼來說,我們選擇用MSDAAB作為一個黑箱組件的實現方法能夠幫助我們防止修改MSDAAB代碼,能夠使我們完美的升級組件,這個新特征是可以實現的。

?

數據傳輸

?????? DotNetNuke用DataReader把從數據訪問層(DAL)那里讀取的數據集合傳遞給業務邏輯層(BLL)。選用DataReader是因為它是ADO.NET提供的租塊的數據傳輸機制(一個只向前的只讀的數據流)。IDataReader是所有.NET兼容的數據讀取器(DataReaders)的基本接口。這個抽象的IDataReader接口使我們能夠在各層之間傳輸數據,而無需考慮數據訪問協議在實際的數據提供者實現中可不可用(例如SqlClient, OleDB, ODBC等)。

?

業務邏輯層 ( BLL )

?????? 好的面向對象設計推薦我們將數據存儲從應用程序中提取(抽象)出來。通過提取可以在應用程序上建立一個獨立的業務邏輯接口集;因此減少了對下面的數據庫物理實現的依賴。

?????? DNN的業務邏輯層被有效的定義在\Components的子文件夾下。業務邏輯層包含了表述層調用的各種應用程序服務的抽象類。在數據訪問方面,業務邏輯層向前調用應用程序接口(API)到適當的數據提供者,這個過程就文檔前面介紹的數據提供者工廠機制。

?????? 自定義業務對象是一個用自定義的數據結構封裝數據的面向對象的技術。自定義業務對象需要一些自定義的代碼,這就在類型安全設計模型、分離數據存儲和序列化方面增加了開銷。自定義業務對象提供了最大程度的靈活性,他們使應用程序可以在它的抽象范圍內定義自己的數據結構;也消除了對所有數據容器的依賴(例如RecordSet, DataSet )。

?????? 什么是類型安全的設計模型呢?考慮一下下面的數據訪問代碼例子:變量= DataReader(“字段名”)。這里將數據庫字段的值賦給了一個變量,這段代碼的問題在于無法保證數據庫字段的數據類型和變量的數據類型相匹配;并且這個過程中的任何錯誤都將提交給運行時(run-time)來處理。采用自定義業務對象那么代碼將是這樣:變量=對象.屬性(variable = Object.Property)。這樣的話編譯器就會在數據不匹配的時候迅速的告知我們。類型安全的設計還可以智能感知和改進代碼的易讀性。

?????? 一組對象我們成為一個集合。在DNN里我們用了標準的動態數組(ArrayList)來描述一組自定義業務對象。ArrayLists是ASP.NET內部對象,它包含了基本集合所需要的所有特征(add,remove,find,iterate)。在ArrayList的特征里我們最重要的特征是它實現了IEnumerable接口,它可以被數據綁定(databound)給ASP.NET web控件(web control)。

?????? DNN的數據訪問層是以DataReader的形式傳遞信息給業務邏輯層的。關于這種實現的一個問題就是為什么DNN用DataReader作為數據傳輸容器(container)來傳輸數據而不直接從DAL層傳遞數據給自定義業務對象。這是因為雖然這兩種方法都是可行的,但我們相信使DAL層從BLL層完全獨立出來是由一些優點的。例如:我們要為自定義業務對象增加一個附加屬性,這種情況下這個屬性僅僅是表述層用到而數據庫中根本不需要,用DNN的方法,DAL層實現方法不需要做任何改動,因為他們對上面的BLL層沒有任何依賴;但是如果DAL直接提供數據給自定義業務對象,所有的DAL層實現都需要重新編譯來符合BLL層結構的需要。

?

?

?

?

自定義業務對象助手 ( CBO )

?????? 為了最小化移植從數據層傳輸來到自定義業務邏輯對象信息的代碼工作量,創建了一個通用的utility類。這個類包含了兩個公共的方法(函數)——一個是返回一個單獨對象的實例,一個是返回一個集合對象(arraylist)。一般來說這個類里定義的每個屬性在Datareader里都有相應的字段對應。這些影射的信息的名稱和數據類型都必須是唯一的。下面的代碼展示了如何用反射將datareader里的數據填充給自定義業務對象然后再關閉datareader。

?

?

??? Public Class CBO

?

??????? Private Shared Function GetPropertyInfo(ByVal objType As Type) As ArrayList

?

??????????? ' Use the cache because the reflection used later is expensive

??????????? Dim objCache As System.Web.Caching.Cache = System.Web.HttpContext.Current.Cache

?

??????????? If objCache(objType.Name) Is Nothing Then

??????????????? Dim objProperties As New ArrayList()

??????????????? Dim objProperty As PropertyInfo

??????????????? For Each objProperty In objType.GetProperties()

??????????????????? objProperties.Add(objProperty)

??????????????? Next

??????????????? objCache.Insert(objType.Name, objProperties)

??????????? End If

?

??????????? Return CType(objCache(objType.Name), ArrayList)

?

??????? End Function

?

??????? Private Shared Function GetOrdinals(ByVal objProperties As ArrayList, ByVal dr As IDataReader) As Integer()

?

????????? ??Dim arrOrdinals(objProperties.Count) As Integer

??????????? Dim intProperty As Integer

?

??????????? If Not dr Is Nothing Then

??????????????? For intProperty = 0 To objProperties.Count - 1

??????????????????? arrOrdinals(intProperty) = -1

?????????????? ?????If CType(objProperties(intProperty), PropertyInfo).CanWrite Then

??????????????????????? Try

??????????????????????????? arrOrdinals(intProperty) = dr.GetOrdinal(objProperties(intProperty).Name)

??????????????????????? Catch

????????????????????????? ??' property does not exist in datareader

??????????????????????? End Try

??????????????????? End If

??????????????? Next intProperty

??????????? End If

?

??????????? Return arrOrdinals

?

??????? End Function

?

??????? Private Shared Function CreateObject(ByVal objType As Type, ByVal dr As IDataReader, ByVal objProperties As ArrayList, ByVal arrOrdinals As Integer()) As Object

?

??????????? Dim objObject As Object = Activator.CreateInstance(objType)

??????????? Dim intProperty As Integer

?

??????????? ' fill object with values from datareader

??????????? For intProperty = 0 To objProperties.Count - 1

??????????????? If arrOrdinals(intProperty) <> -1 Then

??????????????????? If IsDBNull(dr.GetValue(arrOrdinals(intProperty))) Then

??????????????????????? ' translate Null value

??????????????????????? objProperties(intProperty).SetValue(objObject, Null.SetNull(CType(objProperties(intProperty), PropertyInfo)), Nothing)

??????????????????? Else

??????????????????????? Try

??????????????????????????? ' try implicit conversion first

??????????????????????????? objProperties(intProperty).SetValue(objObject, dr.GetValue(arrOrdinals(intProperty)), Nothing)

??????????????????????? Catch ' data types do not match

??????????????????????????? Try

??????????????????????????????? ' try explicit conversion

??????????????????????????????? objProperties(intProperty).SetValue(objObject, Convert.ChangeType(dr.GetValue(arrOrdinals(intProperty)), CType(objProperties(intProperty), PropertyInfo).PropertyType), Nothing)

???????????????????? ???????Catch

??????????????????????????????? ' error assigning a datareader value to a property

??????????????????????????? End Try

??????????????????????? End Try

??????????????????? End If

??????????????? End If

??????????? Next intProperty

?

??????????? Return objObject

?

??????? End Function

?

??????? Public Shared Function FillObject(ByVal dr As IDataReader, ByVal objType As Type) As Object

?

??????????? Dim objFillObject As Object

??????????? Dim intProperty As Integer

?

??????????? ' get properties for type

??????????? Dim objProperties As ArrayList = GetPropertyInfo(objType)

?

??????????? ' get ordinal positions in datareader

??????????? Dim arrOrdinals As Integer() = GetOrdinals(objProperties, dr)

?

??????????? ' read datareader

??????????? If dr.Read Then

??????????????? ' fill business object

??????????????? objFillObject = CreateObject(objType, dr, objProperties, arrOrdinals)

??????????? Else

??????????????? objFillObject = Nothing

??????????? End If

?

??????????? ' close datareader

??????????? If Not dr Is Nothing Then

??????????????? dr.Close()

??????????? End If

?

??????????? Return objFillObject

?

??????? End Function

?

??????? Public Shared Function FillCollection(ByVal dr As IDataReader, ByVal objType As Type) As ArrayList

?

??????????? Dim objFillCollection As New ArrayList()

??????????? Dim objFillObject As Object

??????????? Dim intProperty As Integer

?

??????????? ' get properties for type

??????????? Dim objProperties As ArrayList = GetPropertyInfo(objType)

?

??????????? ' get ordinal positions in datareader

??????????? Dim arrOrdinals As Integer() = GetOrdinals(objProperties, dr)

?

??????????? ' iterate datareader

??????????? While dr.Read

??????????????? ' fill business object

??????????????? objFillObject = CreateObject(objType, dr, objProperties, arrOrdinals)

??????????????? ' add to collection

??????????????? objFillCollection.Add(objFillObject)

??????????? End While

?

??????????? ' close datareader

??????????? If Not dr Is Nothing Then

??????????????? dr.Close()

??????????? End If

?

??????????? Return objFillCollection

?

??????? End Function

?

??? End Class

空處理

?

?????? 每一個數據存取系統都有一個特殊的構造來處理那些沒有明確指定的字段值。

在大多數關系數據庫管理系統中,這個構造就是眾所周知的null值。從應用程序的角度看,在表述層和數據存取層傳遞null值是一個架構上的挑戰。這是因為表述層必須從數據庫的特定信息抽象出來;而且,當一個屬性值沒有明確指定的時候表述層也必須能夠表達說明。事實上這相當復雜,.NET Framework的本身的數據類型不能自動的轉換從數據庫返回的null值(如果你試圖直接那樣賦值的話將會拋出一個異常)。另外,每一個數據存儲都有它自己的屬性來實現null。唯一合理的解決方案就是創建一個抽象的傳輸服務,來編碼/解碼應用程序各層之間的null值。

?

?????? 乍一看,你也許會想到用vb.net中的“nothing”關鍵字可以很好的擔負起這個傳輸服務的任務。不幸的是,調查顯示,.NET Framework本身的數據類型處理“nothing”的時候沒有預想的那么好。盡管分配為nothing的屬性不會拋出異常,實際上這個屬性的值將非常依賴于它的數據類型(String = Nothing, Date = Date.MinValue, Integer = 0, Boolean = False, 等等)并且自帶的IsNothing()函數的結果還不是一致的(兼容的)結果。

?

?????? 在DNN里,我們創建了一個通用的類來處理null的問題,它統一管理應用程序各層的null問題。在應用程序中用一個常量來描述每種數據類型的null情況,再把這個常量轉化成各種數據存儲實現里的實際的null值。這個類包含的各種方法將null轉換服務的物理細節從應用程序中抽象出來了。

?????? * 記住,這個類僅僅用在數據庫字段允許有null值的情況下。還要記住,這個類要求DAL和BLL層之間的數據類型一致(例如:一個BLL信息類里的屬性字段的數據類型必須跟DAL 數據提供者傳遞過來的參數的數據類型一致)。

?

?

??? Public Class Null

?

??????? ' define application encoded null values

??????? Public Shared ReadOnly Property NullInteger() As Integer

??????????? Get

??????????????? Return -1

??????????? End Get

??? ????End Property

??????? Public Shared ReadOnly Property NullDate() As Date

??????????? Get

??????????????? Return Date.MinValue

??????????? End Get

??????? End Property

??????? Public Shared ReadOnly Property NullString() As String

??????????? Get

?????? ?????????Return ""

??????????? End Get

??????? End Property

??????? Public Shared ReadOnly Property NullBoolean() As Boolean

??????????? Get

??????????????? Return False

??????????? End Get

??????? End Property

?

??????? ' sets a field to an application encoded null value ( used in Presentation layer )

??????? Public Shared Function SetNull(ByVal objField As Object) As Object

??????????? If TypeOf objField Is Integer Then

??????????????? SetNull = NullInteger

??????????? ElseIf TypeOf objField Is Date Then

??????????????? SetNull = NullDate

??????????? ElseIf TypeOf objField Is String Then

??????????????? SetNull = NullString

??????????? ElseIf TypeOf objField Is Boolean Then

??????????????? SetNull = NullBoolean

??????????? Else

??????????????? Throw New NullReferenceException()

??????????? End If

??????? End Function

?

??????? ' sets a field to an application encoded null value ( used in BLL layer )

??????? Public Shared Function SetNull(ByVal objPropertyInfo As PropertyInfo) As Object

??????????? Select Case objPropertyInfo.PropertyType.ToString

??????????????? Case "System.Int16", "System.Int32", "System.Int64", "System.Single", "System.Double", "System.Decimal"

??????????????????? SetNull = NullInteger

??????????????? Case "System.DateTime"

??????????????? ????SetNull = NullDate

??????????????? Case "System.String", "System.Char"

??????????????????? SetNull = NullString

??????????????? Case "System.Boolean"

??????????????????? SetNull = NullBoolean

??????????????? Case Else

??????????????????? Throw New NullReferenceException()

??????????? End Select

??????? End Function

?

??????? ' convert an application encoded null value to a database null value ( used in DAL )

??????? Public Shared Function GetNull(ByVal objField As Object, ByVal objDBNull As Object) As Object

??????????? GetNull = objField

??????????? If TypeOf objField Is Integer Then

??????????????? If objField = NullInteger Then

??????????????????? GetNull = objDBNull

??????????????? End If

??????????? ElseIf TypeOf objField Is Date Then

?????????????? ?If objField = NullDate Then

??????????????????? GetNull = objDBNull

??????????????? End If

??????????? ElseIf TypeOf objField Is String Then

??????????????? If objField = NullString Then

??????????????????? GetNull = objDBNull

??????????????? End If

???? ???????ElseIf TypeOf objField Is Boolean Then

??????????????? If objField = NullBoolean Then

??????????????????? GetNull = objDBNull

??????????????? End If

??????????? Else

??????????????? Throw New NullReferenceException()

??????????? End If

??????? End Function

?

??????? ' checks if a field contains an application encoded null value

??????? Public Shared Function IsNull(ByVal objField As Object) As Boolean

??????????? If objField = SetNull(objField) Then

??????????????? IsNull = True

??????????? Else

???? ???????????IsNull = False

??????????? End If

??????? End Function

?

??? End Class

實現細節

?????? 下面的一段代碼例子示范了應用程序的各層之間是如何完成數據訪問的。

?

表述層( UI )

?????? 表述層依賴于它上面的業務邏輯層。自定義業務邏輯對象的屬性和方法建立了這兩個曾之間的基礎接口(表述層不要直接調用數據訪問層的方法)。

?

?

獲取

?

?????? ' create a Controller object

??? Dim objAnnouncements As New AnnouncementsController

?

??? ' get the collection

??? lstAnnouncements.DataSource = objAnnouncements.GetAnnouncements(ModuleId)

??? lstAnnouncements.DataBind()

?

增加/更新

?

??? ...

??? Private itemId As Integer

???

??? If Not (Request.Params("ItemId") Is Nothing) Then

??????? itemId = Int32.Parse(Request.Params("ItemId"))

??? Else

??????? itemId = Null.SetNull(itemId)

??? End If

??? ...

?

??? ' create an Info object

??? Dim objAnnouncement As New AnnouncementInfo

?

??? ' set the properties

??? objAnnouncement.ItemId = itemId

??? objAnnouncement.ModuleId = ModuleId

??? objAnnouncement.CreatedByUser = Context.User.Identity.Name

??? objAnnouncement.Title = txtTitle.Text

??? objAnnouncement.Description = txtDescription.Text

??? objAnnouncement.Url = txtExternal.Text

??? objAnnouncement.Syndicate = chkSyndicate.Checked

??? If txtViewOrder.Text <> "" Then

??????? objAnnouncement.ViewOrder = txtViewOrder.Text

??? Else

??????? objAnnouncement.ViewOrder = Null.SetNull(objAnnouncement.ViewOrder)

??? End If

??? If txtExpires.Text <> "" Then

??????? objAnnouncement.ExpireDate = txtExpires.Text

??? Else

??????? objAnnouncement.ExpireDate = Null.SetNull(objAnnouncement.ExpireDate)

??? End If

?

??? ' create a Controller object

??? Dim objAnnouncements As New AnnouncementsController

?

??? If Null.IsNull(itemId) Then

??????? ' add

??????? objAnnouncements.AddAnnouncement(objAnnouncement)

??? Else

??????? ' update

??????? objAnnouncements.UpdateAnnouncement(objAnnouncement)

??? End If

?

??? ** Notice the use of the Null.SetNull() and Null.IsNull() helper methods

?

刪除

?

?????? ' create a Controller object

??? Dim objAnnouncements As New AnnouncementsController

?

??? ' delete the record

??? objAnnouncements.DeleteAnnouncement(itemId)

?

業務邏輯層( BLL )

?

?????? 每一個應用程序業務方法都有跟它相對應的多關系業務對象組成的物理文件。每一個業務對象定義都有一個定義它的屬性的信息類和定義它的方法的控制類。

?

??? Public Class AnnouncementInfo

?

??????? ' local property declarations

??????? Private _ItemId As Integer

??????? Private _ModuleId As Integer

??????? Private _UserName As String

??????? Private _Title As String

??????? Private _Url As String

??????? Private _Syndicate As Boolean

??????? Private _ExpireDate As Date

??????? Private _Description As String

??????? Private _ViewOrder As Integer

??????? Private _CreatedByUser As String

??????? Private _CreatedDate As Date

??????? Private _Clicks As Integer

?

? ??????' constructor

??????? Public Sub New()

??????? ' custom initialization logic

??????? End Sub

?

??????? ' public properties

??????? Public Property ItemId() As Integer

??????????? Get

??????????????? Return _ItemId

??????????? End Get

??????????? Set(ByVal Value As Integer)

??????????????? _ItemId = Value

??????????? End Set

??????? End Property

?

??????? Public Property ModuleId() As Integer

??????????? Get

??????????????? Return _ModuleId

??????????? End Get

??????????? Set(ByVal Value As Integer)

???????? ???????_ModuleId = Value

??????????? End Set

??????? End Property

?

??????? Public Property Title() As String

??????????? Get

??????????????? Return _Title

??????????? End Get

??????????? Set(ByVal Value As String)

??????????????? _Title = Value

?????????? ?End Set

??????? End Property

?

??????? Public Property Url() As String

??????????? Get

??????????????? Return _Url

??????????? End Get

??????????? Set(ByVal Value As String)

??????????????? _Url = Value

??????????? End Set

??????? End Property

?

??????? Public Property Syndicate() As Boolean

??????????? Get

??????????????? Return _Syndicate

??????????? End Get

??????????? Set(ByVal Value As Boolean)

??????????????? _Syndicate = Value

??????????? End Set

??????? End Property

?

??????? Public Property ViewOrder() As Integer

??????????? Get

??????????????? Return _ViewOrder

??????????? End Get

??????????? Set(ByVal Value As Integer)

??????????????? _ViewOrder = Value

??????????? End Set

??????? End Property

?

??????? Public Property Description() As String

?????? ?????Get

??????????????? Return _Description

??????????? End Get

??????????? Set(ByVal Value As String)

??????????????? _Description = Value

??????????? End Set

??????? End Property

?

??????? Public Property ExpireDate() As Date

??????????? Get

??????????? ????Return _ExpireDate

??????????? End Get

??????????? Set(ByVal Value As Date)

??????????????? _ExpireDate = Value

??????????? End Set

??????? End Property

?

??????? Public Property CreatedByUser() As String

??????????? Get

??????????????? Return _CreatedByUser

??????????? End Get

??????????? Set(ByVal Value As String)

??????????????? _CreatedByUser = Value

??????????? End Set

??????? End Property

?

??????? Public Property CreatedDate() As Date

??????????? Get

??????????????? Return _CreatedDate

??????????? End Get

??????????? Set(ByVal Value As Date)

??????????????? _CreatedDate = Value

??????????? End Set

??????? End Property

?

??????? Public Property Clicks() As Integer

??????????? Get

??????????????? Return _Clicks

??????????? End Get

??????????? Set(ByVal Value As Integer)

??????????????? _Clicks = Value

??????????? End Set

??????? End Property

??? End Class

?

每一個數據庫的字段在信息類里都有相對應的屬性。為了使通用的自定義業務對象助手(CBO Helper)類能自動的把從IDataReader接口獲取的數據轉換成自定義的業務對象,數據庫字段和與它直接關聯的屬性在名稱和數據類型方面都必須是唯一的。

?

?

??? Public Class AnnouncementsController

?

??????? Public Function GetAnnouncements(ByVal ModuleId As Integer) As ArrayList

?

??????????? Return CBO.FillCollection(DataProvider.Instance().GetAnnouncements(ModuleId), GetType(AnnouncementInfo))

?

??????? End Function

?

?

??????? Public Function GetAnnouncement(ByVal ItemId As Integer, ByVal ModuleId As Integer) As AnnouncementInfo

?

??????????? Return CType(CBO.FillObject(DataProvider.Instance().GetAnnouncement(ItemId, ModuleId), GetType(AnnouncementInfo)), AnnouncementInfo)

?

??????? End Function

?

?

??????? Public Sub DeleteAnnouncement(ByVal ItemID As Integer)

?

??????????? DataProvider.Instance().DeleteAnnouncement(ItemID)

?

??????? End Sub

?

??????? Public Sub AddAnnouncement(ByVal objAnnouncement As AnnouncementInfo)

?

?

??????????? DataProvider.Instance().AddAnnouncement(objAnnouncement.ModuleId, objAnnouncement.CreatedByUser, objAnnouncement.Title, objAnnouncement.Url, objAnnouncement.Syndicate, objAnnouncement.ExpireDate, objAnnouncement.Description, objAnnouncement.ViewOrder)

?

??????? End Sub

?

??????? Public Sub UpdateAnnouncement(ByVal objAnnouncement As AnnouncementInfo)

?

??????????? DataProvider.Instance().UpdateAnnouncement(objAnnouncement.ItemId, objAnnouncement.CreatedByUser, objAnnouncement.Title, objAnnouncement.Url, objAnnouncement.Syndicate, objAnnouncement.ExpireDate, objAnnouncement.Description, objAnnouncement.ViewOrder)

?

??????? End Sub

?

??? End Class

?

???? 你可能注意到了傳遞信息到數據庫的控制方法(例如:增加和更新)是傳遞一個自定義業務對象實例作為一個參數的。這樣做的優點是:對象定義從BLL層獨立出來,這樣就減少了類定義改變后相應的修改。個別的對象屬性被提取出來作為數量值傳遞給數據訪問層(這是因為DAL不關心BLL對象的結構)。

?

?

數據訪問層 ( DAL )

?

?????? 采用本文上述介紹的提供者技術DNN可以支持多種數據存儲。實際上它包含一個基類,這個基類在運行時決定哪個具體的數據訪問類適合當前的數據訪問請求。

?

?

DataProvider ( 基類 )

?

?? ?' announcements module

??? Public MustOverride Function GetAnnouncements(ByVal ModuleId As Integer) As IDataReader

??? Public MustOverride Function GetAnnouncement(ByVal ItemId As Integer, ByVal ModuleId As Integer) As IDataReader

??? Public MustOverride Sub DeleteAnnouncement(ByVal ItemID As Integer)

??? Public MustOverride Sub AddAnnouncement(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)

??? Public MustOverride Sub UpdateAnnouncement(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)

?

SqlDataProvider (具體實現類)

?

在具體類中包含了下面的幫助方法,這個方法用來獨立數據庫null的實現(這個例子中DBNull.Value 是針對SQL Server而言的)并且提供一個簡單的接口。

?

?

??? ' general

??? Private Function GetNull(ByVal Field As Object) As Object

??????? Return Null.GetNull(Field, DBNull.Value)

??? End Function

?

每一個在基類里表明必須繼承的方法在具體類里都必須實現。注意上面的add/update方法里描述的GetNull()函數的使用。

?

??? ' announcements module

??? Public Overrides Function GetAnnouncements(ByVal ModuleId As Integer) As IDataReader

??????? Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetAnnouncements", ModuleId), IDataReader)

??? End Function

??? Public Overrides Function GetAnnouncement(ByVal ItemId As Integer, ByVal ModuleId As Integer) As IDataReader

??????? Return CType(SqlHelper.ExecuteReader(ConnectionString, DatabaseOwner & ObjectQualifier & "GetAnnouncement", ItemId, ModuleId), IDataReader)

??? End Function

??? Public Overrides Sub DeleteAnnouncement(ByVal ItemId As Integer)

??????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "DeleteAnnouncement", ItemId)

??? End Sub

??? Public Overrides Sub AddAnnouncement(ByVal ModuleId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)

??????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "AddAnnouncement", ModuleId, UserName, Title, URL, Syndicate, GetNull(ExpireDate), Description, GetNull(ViewOrder))

??? End Sub

??? Public Overrides Sub UpdateAnnouncement(ByVal ItemId As Integer, ByVal UserName As String, ByVal Title As String, ByVal URL As String, ByVal Syndicate As Boolean, ByVal ExpireDate As Date, ByVal Description As String, ByVal ViewOrder As Integer)

??????? SqlHelper.ExecuteNonQuery(ConnectionString, DatabaseOwner & ObjectQualifier & "UpdateAnnouncement", ItemId, UserName, Title, URL, Syndicate, GetNull(ExpireDate), Description, GetNull(ViewOrder))

??? End Sub

緩存

?

?????? 很多數據訪問頻繁的方法采用了web緩存技術通過減少后臺數據庫請求次數的方法來提高性能。System.Web.Caching.Cache命名空間提供一些往緩存中增加內容或從緩存中重新獲取內容的工具。它包含一個字典接口,憑借一個字符串關鍵字來查找對應的對象。這個對象將持續在應用程序的整個生命周期。當應用程序重起后這個緩存才被重新創建。注意,只有序列化的對象才能被加入到緩存里。

?

?

??????????? Dim objCache As System.Web.Caching.Cache = System.Web.HttpContext.Current.Cache

?

??????????? If objCache("GetHostSettings") Is Nothing Then

??????????????? objCache.Insert("GetHostSettings", GetHostSettings())

??????????? End If

??????????? Me.HostSettings = objCache("GetHostSettings")

?

System.Web.Caching.Cache對象還支持緩存管理的一些特征。為防止緩存因為大量的內容項而臃腫不堪,DNN采用一個動態調整的方法來刪除那些超過60秒沒有被訪問的對象。這個特征主要用在有緩存設置的地方。

?

?

??????????? If objCache("GetPortalTabModules" & intTabId.ToString) Is Nothing Then

??????????????? dr = DataProvider.Instance().GetPortalTabModules(Me.PortalId, Me.ActiveTab.TabId)

??????????????? objCache.Insert("GetPortalTabModules" & intTabId.ToString, ConvertDataReaderToDataSet(dr), Nothing, DateTime.MaxValue, TimeSpan.FromSeconds(60))

??????????? End If

?

??????????? ds = objCache("GetPortalTabModules" & intTabId.ToString)

?

當應用程序的操作會影響緩存內容時,每一個受影響的緩存內容項都會被刪除,這樣新的內容項才能加進來。

?

?

??????????? If Not objCache("GetHostSettings") Is Nothing Then

??????????????? objCache.Remove("GetHostSettings")

??????????? End If

?

性能

?????? 為了評測系統的性能我們用了微軟應用程序評測中心(Microsoft Application Center Test)工具,這個工具可以模擬大量的用戶打開很多服務器連接并且快速的提交http請求。為了對比,我們分析了DotNetNuke1.0.10( 采用SqlCommandGenerator )跟DotNetNuke 2.0 ( 采用新的抽象DAL )。下面是測試結果(特別感謝Kenny Rice提供測試援助)。

?

?

DotNetNuke 2.0 ( DAL enhancement )

?

Total number of requests: ??????????????????????????? 93,254

Total number of connections: ?????????????????????? 93,253

?

Average requests per second: ??????????????????????????? 310.85

Average time to first byte (msecs): ???????????????????? 2.37

Average time to last byte (msecs): ???????????????????? 2.46

Average time to last byte per iteration (msecs): 29.58

?

Number of unique requests made in test: ?????????? 12

Number of unique response codes: ??????????????????? 1

?

DotNetNuke 1.0.10 ( SqlCommandGenerator )

?

Total number of requests:????????????????????? 42,350

Total number of connections:??????????????????????? 42,350

?????????????

Average requests per second:????????????????????? 141.17

Average time to first byte (msecs):??????????????? 6.02

Average time to last byte (msecs):??????????????? 6.15

Average time to last byte per iteration (msecs):? 116.94

?????????????

Number of unique requests made in test:??????????? 17

Number of unique response codes:?????????????? 2

開發

?????? DNN提供了靈活的門戶軟件架構。這個應用程序核心提供了大量的服務作為通用的方法,例如會員,角色安全,個性化,管理,站點log,導航和數據存取(訪問)。它還提供了靈活的擴展應用程序增加特殊業務功能的能力。大多數情況下建議將特殊的業務功能從框架核心抽象出來并用自定義模塊實現。這保持了核心的完成性并且為將來升級提供了最好的選擇。但是,如果你絕對必須修改核心實體的話,你也不會受到你的需求變化的限制。

?

自定義模塊

?????? DNN允許將自定義模塊打包成私有的程序集發布到門戶安裝。只要較小的修改,自定義模塊在數據存取方面可以采用相同的技術作為核心。這種方式的另一個優點是能夠為每一個支持的數據庫平臺提供自定義模塊的不同版本。

?????? 在你采用下面論述的數據訪問技術之前,你需要先考慮一下你的組件實際上是否需要支持多種數據庫。DNN不強制你采用提供者模式創建自定義模塊。事實上,如果你清楚你的組件僅僅用在單一的數據庫平臺,那么不需要增加額外的開發努力。開發者作這些決定(判斷)是一種基本的職責(原則)。

?

?

\PrivateAssemblies\Survey

?????? 概觀自定義模塊,它的架構其實跟DNN核心的架構是同樣的方法。它包含了一個叫做SurveyDB.vb的業務邏輯層的類,這個類包含了業務邏輯層的方法。它還包含了它自己的SurveyDataProvider.vb的類(文件名/類名很重要,因為你不想它跟DNN里的DataProvider類沖突)。這種情況下ProviderName常量被設置成上述的類名+ DataProvider字符串值的形式。這對于工廠方法里使用通過相同的配置集中得到的基于相同的數據庫的數據提供者很重要(例如:SurveySqlDataProvider 將會跟SqlDataProvider 使用相同的web.config配置集)。

?

?

Imports System

Imports System.Web.Caching

Imports System.Reflection

?

Namespace DotNetNuke

?

??? Public MustInherit Class SurveyDataProvider

?

??????? ' unique provider name used for caching

??????? Private Const ProviderType As String = "data"

??????? Private Const ProviderName As String = "Survey"

?

而且象DNN里的DataProvider類一樣,它包含了必須的數據存取方法。

?

?????? ?Public MustOverride Function GetSurveys(ByVal ModuleId As Integer) As IDataReader

??????? Public MustOverride Function GetSurvey(ByVal SurveyID As Integer, ByVal ModuleId As Integer) As IDataReader

??????? Public MustOverride Sub AddSurvey(ByVal ModuleId As Integer, ByVal Question As String, ByVal ViewOrder As String, ByVal OptionType As String, ByVal UserName As String)

??????? Public MustOverride Sub UpdateSurvey(ByVal SurveyId As Integer, ByVal Question As String, ByVal ViewOrder As String, ByVal OptionType As String, ByVal UserName As String)

??????? Public MustOverride Sub DeleteSurvey(ByVal SurveyID As Integer)

??????? Public MustOverride Function GetSurveyOptions(ByVal SurveyId As Integer) As IDataReader

??????? Public MustOverride Sub AddSurveyOption(ByVal SurveyId As Integer, ByVal OptionName As String, ByVal ViewOrder As String)

??????? Public MustOverride Sub UpdateSurveyOption(ByVal SurveyOptionId As Integer, ByVal OptionName As String, ByVal ViewOrder As String)

??????? Public MustOverride Sub DeleteSurveyOption(ByVal SurveyOptionID As Integer)

??????? Public MustOverride Sub AddSurveyResult(ByVal SurveyOptionId As Integer)

?

\database

?

?????? 自定義模塊包含數據提供者的實現,我們必須再一次在web.config文件里指定一個自定義屬性定義的實現。

?

Imports System

Imports System.Data

Imports System.Data.SqlClient

Imports Microsoft.ApplicationBlocks.Data

?

Namespace DotNetNuke.Data

?

??? Public Class SurveySqlDataProvider

?

??????? Inherits SurveyDataProvider

?

??????? Private Const ProviderType As String = "data"

?

??????? Private _providerConfiguration As ProviderConfiguration = ProviderConfiguration.GetProviderConfiguration(ProviderType)

??????? Private _connectionString As String

??????? Private _providerPath As String

??????? Private _objectQualifier As String

??????? Private _databaseOwner As String

?

Survey.dnn ( deployment )

?

?????? 為了發布私有的自定義模塊,DNN用到了一個清單文件。增加多個提供者的時候這個文件的結構有些微的變化。

?

?

<module>

? <folder>DesktopModules/Survey</folder>

? <friendlyname>Survey</friendlyname>

? <desktopsrc>Survey.ascx</desktopsrc>

? <mobilesrc></mobilesrc>

? <editsrc>EditSurvey.ascx</editsrc>

? <description>Survey allows you to create custom surveys to obtain public feedback</description>

? <editmoduleicon>icon_survey_32px.gif</editmoduleicon>?

? <uninstall></uninstall>?

? <files>

??? <file>

????? <name>Survey.ascx</name>

??? </file>

??? <file>

????? <name>EditSurvey.ascx</name>

??? </file>

??? <file>

????? <name>Survey.dll</name>

??? </file>

??? <file>

????? <name>DotNetNuke.SurveySqlDataProvider.dll</name>

??? </file>

??? <file>

????? <name>Survey01.00.00.SqlDataProvider</name>

??? </file>

??? <file>

????? <name>Survey Uninstall.SqlDataProvider</name>

??? </file>

??? <file>

????? <name>DotNetNuke.SurveyAccessDataProvider.dll</name>

??? </file>

??? <file>

????? <name>Survey01.00.00.AccessDataProvider</name>

??? </file>

??? <file>

?? ???<name>Survey Uninstall.AccessDataProvider</name>

??? </file>

? </files>

</module>

?

改進核心模塊

?????? 自定義模塊是為門戶架構增加附加功能的首選方法。當然,有時候修改核心功能來滿足你特定的需求也是必需的。為了能修改核心模塊的數據訪問方法,你必須對控制管理提供者模型的面向對象原則有最基本的了解。

?????? 理論上,這個提供者模型采用一個基類派生出實例化的子類的工廠設計模式。事實上,DataProvider類就起這個基類的作用,它定義了所有應用程序的核心數據存取方法。所有的方法被定義成公共的必須繼承的,也就是說它們在基類中只是實簡單的定義而且都沒有實現。

?????? DataProvider類起到一個契約的作用,這個契約要求它的子類都必須完全實現,否則對象的實例化將會失敗。這個意思也就是說如果一個基類要求必須被繼承的方法的參數列表或返回值被修改,那么所有子類的實現也都必須作相應的修改,否則它們不會被正確的上載。不能正確的上載不僅意味著某個特殊方法的調用會失敗,實際上,子類的正確實例化也將徹底失敗。這個契約機制雖然給應用程序帶來一定的弱點,但是它保證了每個子類實現有了最小的標準。

?

?????? 下面的例子示范了有關擴展核心的步驟,假定我們增加一個新的字段到核心表中:

?

1、? 如果需要,修改表述層來顯示和修改新的字段。

2、? 修改相關聯的業務邏輯層的類,在相關方法中添加這個字段(例如AddTable, UpdateTable)。

3、? 修改DataProvider基類因步驟2中變化而帶來的必須的變化,并重新編譯應用程序。

4、? 為每一個DataProvider的子類實現(例如:SqlDataProvider,AccessDataProvider)做必要的修改,重新編譯會顯示基類跟其實現類之間的差異。需要修改的實現類的數量依賴于你的應用支持的不同數據庫的數量。

5、用特殊的數據庫更新命令(如:ALTER TABLE)修改每一個DataProvider子類實現的腳本。如果數據庫提供者用了存儲過程,那么同樣必須寫新版本的存儲過程(用相關的DROP 和CREATE 命令)。

?

SQL命令發生器(SqlCommandGenerator

?????? 早期版本的DNN包含一個叫做SqlCommandGenerator的類,這個類能夠簡單的調用SQL Server / MSDE數據庫。然而它是反射每一個數據庫調用的,這帶來很嚴重的性能影響。這個類雖然仍然還保留著,但很明顯的我們鼓勵開發者采用DataProvider的模式。

?

?

參考

?????? 微軟ASP.NET 組的Rob Howard提供了大量的指導和DataProvider模型實現的示例代碼,.NET Pet Shop 3.0也提供了很多優秀的參考資料。Microsoft Data Access Application Block一個快速開發SQL Server Provider實現的一個有效的工具。

轉載于:https://www.cnblogs.com/Alaric/archive/2011/12/23/2299787.html

總結

以上是生活随笔為你收集整理的DNN 数据访问策略 (转)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

亚洲精品视频www | 免费观看一级成人毛片 | 久久精品国产99国产 | 国产视频在线观看一区 | 丁香六月激情 | 永久av免费在线观看 | 人人射人人澡 | 国产麻豆精品久久一二三 | 九九热在线播放 | 久久精品1区 | 亚洲最快最全在线视频 | 又黄又爽又湿又无遮挡的在线视频 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 亚洲经典中文字幕 | 91九色蝌蚪视频网站 | 91超碰免费在线 | 91高清不卡 | 日韩欧美综合精品 | 欧美精品一区二区免费 | 一本一本久久a久久精品综合 | 黄色小说在线免费观看 | 92精品国产成人观看免费 | 久久亚洲综合国产精品99麻豆的功能介绍 | 国产亚洲精品女人久久久久久 | 国产一卡久久电影永久 | 久久综合免费视频影院 | 九九九热精品免费视频观看网站 | 香蕉视频18 | 狠狠色噜噜狠狠狠狠2021天天 | 久久视频这里有久久精品视频11 | 国产精品 中文在线 | 国产人免费人成免费视频 | 日韩在线视频精品 | 夜夜操狠狠操 | 亚洲综合视频网 | 超碰电影在线观看 | 久久躁日日躁aaaaxxxx | 国产精品一区二区果冻传媒 | 国产专区免费 | 国产91精品一区二区麻豆亚洲 | 国产美腿白丝袜足在线av | 高清中文字幕av | 高清日韩一区二区 | 成人午夜精品 | 天天干天天草 | 99精品在线观看 | 国产色啪 | 片网址| 日韩一区二区三区免费视频 | 中文字幕在线播放日韩 | 96国产精品| 五月天色中色 | 深爱开心激情网 | 日韩三级视频在线观看 | 日韩精品无 | 久久免费视频在线观看30 | 国产精品99久久久久久武松影视 | 伊人黄| 丁香综合av | 黄色成人91 | 成人资源在线 | 一区二区三区在线免费播放 | 97**国产露脸精品国产 | 狠狠狠狠狠狠狠 | 色综合久久久 | 国产人免费人成免费视频 | 97人人澡人人爽人人模亚洲 | 色综合亚洲精品激情狠狠 | 人人网av | 国产黄色在线网站 | 91成熟丰满女人少妇 | 亚洲伊人成综合网 | 国产精品一区二区在线观看 | 久久精品影视 | 麻豆国产视频下载 | 亚欧洲精品视频在线观看 | 久久这里只有精品9 | 日韩中文字幕免费看 | 欧美日韩国产在线一区 | 黄毛片在线观看 | 国产在线视频导航 | www.夜夜爱| 久久亚洲私人国产精品 | 99精品色| 亚洲欧洲国产日韩精品 | 国产精品午夜8888 | 国产精品毛片一区二区在线看 | 欧美日韩在线观看视频 | 欧美性色网站 | 久久久久亚洲最大xxxx | 夜夜夜夜爽 | 亚洲精品大片www | 亚洲欧洲成人 | 在线香蕉视频 | 亚洲人在线7777777精品 | 天天曰夜夜爽 | 精品亚洲在线 | 婷婷色社区 | 色综合激情网 | 亚洲精品男人的天堂 | 久久99精品国产麻豆宅宅 | av网址在线播放 | 亚洲精品在线二区 | 人人爽久久久噜噜噜电影 | 国产一级三级 | 黄色tv视频| 久久久久国产精品厨房 | 九色在线 | 国产91粉嫩白浆在线观看 | 免费在线播放黄色 | 日韩免费电影网站 | 午夜视频色 | 99热这里有| 91精品黄色 | 8090yy亚洲精品久久 | 亚洲欧美精品在线 | 国产超碰在线 | 日韩高清在线不卡 | 欧美精品久久久久a | 欧美激情h| 欧美aaa一级 | 亚洲人xxx | 成年人av在线播放 | 久久激五月天综合精品 | 在线色吧 | 激情网站免费观看 | 92国产精品久久久久首页 | 99精品欧美一区二区 | 国产在线第三页 | 中文字幕一区二区三区四区久久 | 亚洲成a人片77777潘金莲 | 日本高清xxxx| 免费看的黄色 | av久久在线 | 免费精品在线 | 超碰人人99 | 激情欧美丁香 | 这里只有精品视频在线观看 | zzijzzij亚洲成熟少妇 | 伊人超碰在线 | www.色国产 | 日韩电影一区二区在线观看 | 一区中文字幕电影 | 久草视频免费观 | 四虎成人免费观看 | 国产一级片一区二区三区 | www.狠狠色 | 久久激五月天综合精品 | 99热播精品 | 国产免费二区 | 综合色在线| 久久精品综合视频 | 欧美日韩高清一区二区 | 正在播放国产一区 | 91人人爽久久涩噜噜噜 | 欧美日韩国产精品一区二区 | 女人18片毛片90分钟 | 69精品视频| 亚洲在线黄色 | 一区二区三区影院 | 激情六月婷婷久久 | 你操综合 | 国产成人精品午夜在线播放 | 探花视频在线观看免费 | 精品国偷自产国产一区 | 成人av午夜 | 九色精品免费永久在线 | 一区二区在线不卡 | 亚洲一级特黄 | 久久视频精品在线观看 | 色香蕉在线| 高清免费在线视频 | 99中文字幕在线观看 | 在线播放 日韩专区 | 一区二区三区国 | 国产精品久久久久久久久久白浆 | 中文字幕文字幕一区二区 | 玖玖视频国产 | 日韩电影在线观看中文字幕 | 在线中文字幕一区二区 | 亚洲一区二区高潮无套美女 | 日韩有码专区 | 日韩精品一区二区三区不卡 | 亚洲综合在线一区二区三区 | 日韩二区三区 | 久久精品激情 | 久久a久久 | 国产精品国产三级国产aⅴ无密码 | 亚洲蜜桃在线 | 日本黄色大片儿 | 在线中文字母电影观看 | 亚洲一区二区三区四区在线视频 | 亚洲成人av电影 | 欧美日韩国产在线观看 | 天天插天天操天天干 | 九九免费在线观看视频 | 久久久久久久免费 | 中文字幕av有码 | 免费视频 三区 | 久久久精品久久 | 欧洲精品视频一区二区 | 成 人 黄 色 片 在线播放 | 日韩免费高清 | 国产高清视频在线播放一区 | 亚洲狠狠干 | 最近中文字幕大全 | 九九涩涩av台湾日本热热 | 国产乱码精品一区二区蜜臀 | 国产精品免费久久久久久久久久中文 | 亚洲人天堂 | 日韩xxxbbb | 色婷婷综合久久久中文字幕 | 日韩专区视频 | 在线观看免费色 | 久久综合九色综合欧美就去吻 | 在线观看成人毛片 | 91在线看视频免费 | 在线免费视频你懂的 | 在线中文字幕一区二区 | 九九九九九九精品任你躁 | 久久综合9988久久爱 | 亚洲欧美视频在线播放 | 免费看成人片 | 香蕉在线观看视频 | 一级一级一片免费 | 97视频免费看 | 国产成人性色生活片 | 国产91欧美| 天天射天 | 91av短视频 | 久久久久一区二区三区四区 | 成人一区二区在线观看 | 国产又粗又长的视频 | 国产精品久久久久久久久久东京 | 天天色天天射天天综合网 | 国产亚洲精品中文字幕 | 国产精品久久久久久五月尺 | 狠狠干五月天 | 国产精品成人av在线 | 日日夜夜天天久久 | 久精品视频免费观看2 | 久久久久福利视频 | 免费一级片观看 | 欧美亚洲专区 | 国产中文在线视频 | 久久9精品| 久久中文字幕在线视频 | av在线色| 久久精品高清视频 | 日韩欧美69 | 黄色成人在线观看 | 国产精品福利久久久 | 欧美一级性生活片 | 天天干天天操天天爱 | 欧美日韩中文另类 | 天天插天天射 | 久久久久久久久久久网 | 久久久久久久av | 在线天堂日本 | 亚洲成人二区 | 久久好看| 国产老熟| 成人一区影院 | 国产xxxxx在线观看 | 中文字幕免费观看全部电影 | 日韩一区二区三免费高清在线观看 | 五月天综合网 | 国产日产精品一区二区三区四区的观看方式 | www.天天射 | 亚洲另类视频在线观看 | 日本老少交 | 国产精品久久久久久久免费大片 | 亚洲精品国偷拍自产在线观看蜜桃 | 欧美日韩国产一区 | 丁香六月在线 | 日韩mv欧美mv国产精品 | 久久狠狠亚洲综合 | 久久精品免费观看 | 在线看一区二区 | 国产资源在线视频 | 久久免费av电影 | 一级性视频 | 91国内在线| 久久a免费视频 | 国产精品第 | 91在线视频精品 | 国产精品第十页 | 麻豆系列在线观看 | 中文字幕免费中文 | 国产精华国产精品 | 69国产精品成人在线播放 | 麻豆国产在线播放 | 国产在线精 | 日韩av高清在线观看 | 日本中文字幕高清 | 亚洲视频免费在线观看 | 国产人成一区二区三区影院 | 国产这里只有精品 | 黄色片网站大全 | 中文字幕一区二区三区四区久久 | 国产二区精品 | 在线观看一二三区 | 97在线精品国自产拍中文 | 99国产精品一区二区 | 欧美在线一级片 | 视频在线观看入口黄最新永久免费国产 | 久久综合久久综合久久 | 色婷婷六月天 | 欧美一区二区三区在线播放 | 91亚洲精品国偷拍 | 亚洲精品免费在线观看视频 | 国产成人黄色在线 | 亚洲精品一区二区三区高潮 | 成年人黄色免费视频 | 欧美性大战 | 日韩大片在线播放 | 久久久这里有精品 | 日韩精品在线看 | av资源免费观看 | 国产手机视频在线播放 | 99国产情侣在线播放 | 久久久99国产精品免费 | 1024手机看片国产 | www178ccom视频在线 | 日韩在线高清免费视频 | 九九在线免费视频 | 日韩在线视频不卡 | 99亚洲精品在线 | 99久久精品电影 | 国产又粗又猛又黄又爽视频 | 午夜精品久久久久久久久久久久 | 99色婷婷| 黄色一级性片 | 日韩在线视频在线观看 | 久草在线欧美 | 精品国产成人av | 国产精品久久久久久久99 | 超碰在线中文字幕 | 九九九在线观看 | 欧美乱熟臀69xxxxxx | 人人干网| 欧美色图30p| 国产美女被啪进深处喷白浆视频 | 黄色在线网站噜噜噜 | 日韩免费视频线观看 | 久久久久影视 | 亚洲最大免费成人网 | 超碰99在线 | 又污又黄的网站 | 国产成人黄色网址 | 亚洲一区二区精品在线 | 国产成人精品a | 色偷偷网站视频 | 一区在线观看 | 国产一区二区三区在线免费观看 | 久久国产精品99久久久久久老狼 | 手机在线黄色网址 | 国产精品美女久久 | 中文字幕在线视频第一页 | 91香蕉视频污在线 | 婷婷视频导航 | 午夜av免费在线观看 | 四虎在线视频免费观看 | 国产一级精品视频 | 波多野结衣一区 | 一级a性色生活片久久毛片波多野 | 香蕉视频网址 | 国产视频黄 | 99精品在线免费观看 | 欧美精品v国产精品v日韩精品 | 五月综合久久 | 日本最新中文字幕 | 国产在线观看你懂得 | 国产精品成人一区二区三区吃奶 | 日韩欧美网站 | 最新中文字幕在线观看视频 | 97av影院 | av字幕在线| 亚洲v欧美v国产v在线观看 | 二区视频在线 | 久久久国产一区二区三区四区小说 | free. 性欧美.com | 激情网婷婷 | 麻豆视频在线免费观看 | 久草久热 | www五月天com | 国产在线观看污片 | 天天插天天狠天天透 | 欧美在线91 | 中文字幕第一页在线播放 | 国产不卡在线观看视频 | 日韩欧美在线综合网 | 国产日韩精品在线观看 | 久久精品网站视频 | 最新三级在线 | 久产久精国产品 | 天天操天天操天天操天天操天天操天天操 | 日韩艹 | 日韩在线视频免费观看 | 亚洲美女视频在线 | 黄色中文字幕 | 久久精品99国产精品酒店日本 | 色播五月激情综合网 | 欧美日韩国产一区 | 国产探花 | 四虎在线免费观看 | 亚洲一区二区精品视频 | 最近中文字幕第一页 | 新av在线| 国产高清一区二区 | 欧美日韩视频免费看 | 五月天婷婷在线播放 | 久久亚洲综合国产精品99麻豆的功能介绍 | 免费看黄在线看 | 日韩欧美网址 | 丁香久久婷婷 | 91精品资源 | 一本一道久久a久久精品 | 天天操夜夜干 | 国产一级一级国产 | 尤物九九久久国产精品的分类 | 日日干天天爽 | 激情丁香 | 人人干人人模 | 蜜臀av在线一区二区三区 | 欧美a级在线免费观看 | 91香蕉视频在线下载 | 99久久精品免费看国产一区二区三区 | 亚洲一级久久 | 91精品伦理| 亚洲在线视频网站 | 成人教育av | 99精品视频免费观看视频 | 99精品美女| 青春草视频 | 91精品久久久久久久久 | 国产精品自在欧美一区 | jizz999| 五月激情片 | 日本免费一二三区 | 国产色小视频 | 狂野欧美激情性xxxx | 免费在线观看日韩视频 | 中文字幕国产一区 | 中文字幕成人av | 在线v| 嫩草伊人久久精品少妇av | 97av影院 | 国产成人黄色网址 | 一区二区不卡视频在线观看 | 国产麻豆视频免费观看 | 色多多污污 | 色综合天天狠天天透天天伊人 | 午夜视频免费播放 | 夜夜天天干 | 亚洲人成免费网站 | 亚洲精品2区 | 免费在线观看av片 | 久久看免费视频 | 在线中文字幕电影 | 欧美久久久久久久久久久 | 在线观看中文字幕第一页 | 久久久久久久久久久久影院 | 黄色日视频| 免费日韩一区二区三区 | 久热超碰 | 毛片一级免费一级 | 亚洲一区 av | 91精品夜夜 | 国产丝袜网站 | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产精品综合久久久久 | 黄色三级免费观看 | 亚洲精品在线观看免费 | 91在线免费视频观看 | 9色在线视频 | 国产精品11| 综合亚洲视频 | 亚洲欧美成人在线 | 精品在线免费视频 | 国产色拍| 蜜臀av性久久久久蜜臀aⅴ涩爱 | www..com黄色片 | 蜜臀av麻豆 | 亚洲国产中文字幕在线观看 | 日韩高清成人在线 | 999久久久久 | 欧美日韩不卡在线 | 日韩欧美极品 | 欧美精品久久久久久 | 永久av免费在线观看 | 天天操天天干天天插 | 一二区av | 麻豆国产精品va在线观看不卡 | 久久久久中文字幕 | 午夜在线免费视频 | 久久国产精品一区二区 | 国产91aaa| 亚洲日本色 | 国产精品视频永久免费播放 | 亚洲永久在线 | 日日夜夜骑 | 九九久久久久久久久激情 | 久久热亚洲 | 二区三区av | 91看片成人| 国产精品久久久亚洲 | 亚洲九九九在线观看 | 日韩有码在线观看视频 | 精品资源在线 | 狠狠色网 | 成人99免费视频 | 久草视频播放 | 日本视频不卡 | 中文字幕av在线免费 | 在线免费观看视频一区二区三区 | 成人18视频| 国产一区电影在线观看 | 黄色午夜网站 | 日本在线观看中文字幕无线观看 | 久草 | 日韩免费电影网 | 97精产国品一二三产区在线 | 综合在线亚洲 | 国产福利在线免费观看 | 激情av在线播放 | 在线黄色国产电影 | 日韩中文字幕免费看 | 在线国产观看 | 精品久久久久久久久久久久久久久久 | 欧美日韩一区二区久久 | 成人av片免费看 | 伊人网综合在线观看 | 四虎国产精品成人免费4hu | 日韩二级毛片 | 国产色道 | 色瓜 | 亚洲专区欧美 | 久久夜色网 | 一区二区三区中文字幕在线 | 69中文字幕 | 日本h视频在线观看 | 激情综合网天天干 | 亚洲1级片 | 国产在线观看中文字幕 | 精品欧美乱码久久久久久 | 中文字幕在线视频免费播放 | 亚洲无在线 | 日本中文字幕在线一区 | 色婷婷综合激情 | 福利视频第一页 | 久久久久女教师免费一区 | 人人狠| 黄色毛片在线观看 | 国产成人久久精品一区二区三区 | 亚洲综合爱 | 国产精品不卡 | 中文字幕在线观看你懂的 | 亚洲在线a| 最近中文字幕免费视频 | 激情视频一区二区 | www五月天com | 成人免费观看网址 | 欧美日韩性生活 | 久久久精品网站 | 成人午夜网 | www.亚洲精品 | 97av精品 | 久久免费公开视频 | 日韩电影在线观看中文字幕 | 国产二区电影 | 亚洲精品资源在线观看 | 天天天天色射综合 | 在线观看日韩一区 | 天天操天天添 | 久草视频在线观 | 久久av在线| 欧美精品一区二区在线观看 | 热久久最新地址 | 人人狠狠综合久久亚洲婷 | av手机在线播放 | 亚洲综合一区二区精品导航 | 国产精品爽爽久久久久久蜜臀 | 蜜桃av人人夜夜澡人人爽 | 99re国产视频 | 久久久穴 | 91大神免费在线观看 | 日韩xxxbbb| 国产精品一区二区av影院萌芽 | 午夜精品久久久久久久99婷婷 | 麻豆一区二区三区视频 | 国产中文字幕在线看 | 999成人国产 | 午夜久久影视 | 天天综合天天做 | 中文字幕亚洲综合久久五月天色无吗'' | 久久久亚洲网站 | 婷婷激情影院 | 国产亚洲久一区二区 | 亚洲国产精品一区二区久久,亚洲午夜 | 看片一区二区三区 | 久久99久久99精品免观看软件 | 狠狠狠干 | 久久永久视频 | 亚洲高清av | 国产又粗又硬又长又爽的视频 | 正在播放国产一区 | 天天操狠狠操 | 97看片吧| 日韩精品在线视频 | 日本中文字幕在线视频 | 国产一区欧美日韩 | 在线观看国产永久免费视频 | 国产亚洲一级高清 | 91视频在线观看免费 | 国产在线视频导航 | 日日干天天操 | 九九涩涩av台湾日本热热 | 亚洲一级片 | 色橹橹欧美在线观看视频高清 | 成人国产精品久久久久久亚洲 | 婷婷伊人综合亚洲综合网 | 中文字幕乱码电影 | 久久久免费看视频 | 国产日韩精品一区二区三区在线 | 伊人干综合 | 欧美日韩国产色综合一二三四 | 午夜精品中文字幕 | 超碰97中文 | 在线电影日韩 | 日韩av在线不卡 | 最近日本韩国中文字幕 | 热精品| 狠狠干.com | 黄色三级免费网址 | 天天艹天天 | 国产免费二区 | 亚州av网站 | 色五月成人 | 激情欧美日韩一区二区 | 欧美午夜a | 久久在线精品 | 99久久www | 91视频在线看| 中文字幕 成人 | 91看片淫黄大片在线播放 | 91在线国产观看 | 久久免费的精品国产v∧ | a视频免费在线观看 | 久草在线久草在线2 | 亚洲波多野结衣 | 久久国产精品区 | 欧美日韩亚洲在线观看 | 精品毛片一区二区免费看 | 麻豆国产精品视频 | 激情在线免费视频 | 一区二区三区电影大全 | 中文字幕日韩精品有码视频 | 亚洲作爱视频 | 成人av在线直播 | 中文字幕精品三级久久久 | 在线成人观看 | 国产成人99久久亚洲综合精品 | 日韩欧美国产精品 | 国产一级大片免费看 | 久草在线最新 | 一区二区三区免费在线 | 亚洲九九九在线观看 | 午夜在线资源 | 黄视频网站大全 | 五月天久久综合 | 午夜av不卡 | 欧美日韩一级久久久久久免费看 | 中日韩欧美精彩视频 | 免费在线观看av | 亚洲精品国 | 免费看一级黄色大全 | 国产福利网站 | 91香蕉视频黄 | 三级黄色在线观看 | 欧美性猛片 | 麻豆综合网| 超碰精品在线 | 五月婷丁香网 | 国产福利a| 国产日韩欧美在线影视 | 久久乱码卡一卡2卡三卡四 五月婷婷久 | ww亚洲ww亚在线观看 | 黄色特一级 | 亚洲精品国产精品乱码在线观看 | 国产 在线 高清 精品 | 日韩在线观看第一页 | 久久久999免费视频 日韩网站在线 | 伊人狠狠色丁香婷婷综合 | 午夜影院一级 | 成片免费观看视频 | 美女视频黄的免费的 | 国产精品毛片一区二区在线 | 久久精品欧美视频 | 91九色视频在线观看 | 免费日韩av电影 | 不卡视频一区二区三区 | 久久伊人八月婷婷综合激情 | 久久99深爱久久99精品 | 免费看av在线 | 久久精品中文字幕免费mv | 国产精品99久久久久的智能播放 | 国产专区视频在线观看 | 亚洲精品久久久久久国 | 丝袜美女在线观看 | 国产做aⅴ在线视频播放 | 国产中文在线播放 | 97av视频| 一区二区三区电影 | 日韩三级免费观看 | 亚洲国产字幕 | 欧美精品视 | 久久国产精品偷 | 99精品视频免费全部在线 | 美女久久久久久久久久 | 精品影院 | 国内精品美女在线观看 | 精品一区电影 | 香蕉视频在线免费 | 国产99免费视频 | 一级黄毛片 | 国产精品久久久久久久免费大片 | 亚洲成人资源在线观看 | 麻豆精品视频在线观看免费 | 蜜臀av性久久久久av蜜臀三区 | 69夜色精品国产69乱 | 美女国产 | 日韩精品首页 | 欧美日韩国产在线 | 美女视频久久黄 | 日日夜夜天天射 | 国产精品久久久久影视 | 色干干 | 中文在线免费一区三区 | 91av久久 | 黄色精品国产 | 久久影院中文字幕 | 黄色小说免费在线观看 | 2018亚洲男人天堂 | 国产传媒一区在线 | 国产一区二区在线免费 | 91香蕉视频 mp4| 日韩av成人在线观看 | 免费a v在线 | 91色视频| 久久久久免费网 | 欧美一区二区三区激情视频 | 97偷拍视频| 亚洲成a人片在线观看网站口工 | 亚洲视频播放 | 国产91影院| 国产精品岛国久久久久久久久红粉 | 成人在线观看你懂的 | 韩国av免费观看 | 久久噜噜少妇网站 | 国产日韩精品在线 | 91精品国产91 | 亚洲精品ww | 国产成人精品一区二区在线 | 国产中文字幕亚洲 | 午夜精品av | 91成人午夜 | 亚洲精品国产拍在线 | 久久视频一区 | 又粗又长又大又爽又黄少妇毛片 | 国产96在线 | 国产日韩中文在线 | av黄网站| 波多野结衣日韩 | 一区二区不卡 | 99热精品视 | 日韩一区精品 | 日韩精品免费专区 | 国产原厂视频在线观看 | 欧美伦理一区二区三区 | 亚洲成人精品影院 | 97超级碰碰碰视频在线观看 | 日韩城人在线 | 国产精品免费小视频 | av成人黄色 | 在线观看va | 欧美在线观看小视频 | 中文字幕乱码电影 | 成人黄色大片在线观看 | 国产成人精品av久久 | 亚洲精品久久久久中文字幕二区 | 久久草| 国内精品久久久久久久久久清纯 | 天天干,夜夜爽 | 麻豆国产露脸在线观看 | 国产精品久久久久三级 | 成人在线免费看视频 | 色就色,综合激情 | 亚洲精品久久久久58 | 久99视频 | 亚洲第一色| 91传媒视频在线观看 | 久久在线免费 | 综合色婷婷 | 色91在线| 在线看小早川怜子av | 国产精品不卡在线 | 亚洲色图色 | 久久福利影视 | 日本字幕网 | 午夜12点 | www.91成人 | 97av.com| 美女免费视频网站 | 五月激情五月激情 | 日韩欧在线 | 韩国av一区| 一区二区三高清 | 在线涩涩| 久久精品国产免费看久久精品 | 天天色天天色天天色 | 在线免费日韩 | 婷婷在线免费视频 | 丁香久久五月 | 黄色影院在线观看 | 久久久亚洲影院 | 国产精品手机在线 | 久久免费视频在线观看 | 久久色网站 | aaawww| 成人香蕉视频 | 正在播放国产一区 | 日批视频在线观看免费 | 97超碰人| 亚洲国产美女久久久久 | 久久av网址 | 中文理论片 | 9在线观看免费高清完整版在线观看明 | 日韩欧美电影 | 97高清视频 | 国产资源在线播放 | 中文字幕永久在线 | 国产黄色观看 | 黄色精品久久久 | 国产中文a | 91三级视频 | 国产精品视频地址 | 黄色免费在线看 | 天天操天天能 | 亚洲色图美腿丝袜 | 看毛片网站 | av资源中文字幕 | 久久一区国产 | 视频在线观看入口黄最新永久免费国产 | 在线播放精品一区二区三区 | 国产精品久久久久婷婷 | 国产成人三级一区二区在线观看一 | 国产成人久久精品一区二区三区 | 射射射综合网 | 91视频免费网址 | 999在线精品| 在线视频免费观看 | 亚洲最大av网 | 久久成人欧美 | 久久久久国产a免费观看rela | 欧美日韩18 | 91豆麻精品91久久久久久 | 国产999精品久久久久久 | 欧美少妇18p | 欧美日韩免费视频 | 亚洲精欧美一区二区精品 | 99视频在线观看一区三区 | 黄色91免费观看 | 日韩极品视频在线观看 | 久久综合九色综合欧美就去吻 | 日本在线视频网址 | 国产香蕉视频在线播放 | 国产美女网| 天天摸夜夜操 | 亚洲另类人人澡 | www.精选视频.com | 在线观看av网站 | 中文字幕中文字幕在线中文字幕三区 | 婷婷丁香激情综合 | 欧亚日韩精品一区二区在线 | 精品国产一区在线观看 | 精品国产激情 | 国产精品久久久777 成人手机在线视频 | 91丨九色丨蝌蚪丰满 | 久久大片| 日韩国产精品一区 | 亚洲精品久久久久999中文字幕 | 亚洲成av人片在线观看www | 欧美一级性生活视频 | 四虎国产精品免费观看视频优播 | 97综合网| 国产精品igao视频网入口 | 国产精品密入口果冻 | 久久高清免费 | 伊人婷婷网 | 国产精品孕妇 | 手机色在线 | 99视频在线观看一区三区 | 日本精品在线 | 欧美成人亚洲 | 国产精品精 | 午夜视频在线观看一区二区三区 | 中文字幕日韩一区二区三区不卡 | 免费网站黄色 | 激情久久伊人 | 麻豆91在线 | 国产精品国产三级国产aⅴ9色 | 久草精品在线播放 | 精品久久久久久久久亚洲 | 久久成人高清视频 | 久久av福利 | 亚洲激情一区二区三区 | 久久久久97国产 | 免费观看特级毛片 | 色噜噜色噜噜 | 天天操夜操| 欧美一二三区在线观看 | 亚洲午夜av久久乱码 | 久草成人在线 | 久久综合狠狠综合 | 99视频在线观看免费 | 在线观看国产日韩欧美 | 欧美国产日韩在线视频 | 精品一区二区免费 | 精品国产亚洲日本 | 国产一区欧美一区 | 中文字幕乱在线伦视频中文字幕乱码在线 | 国产 成人 久久 | 亚洲一区视频在线播放 | 亚洲精品视频免费看 | 在线观看欧美成人 | 欧美一进一出抽搐大尺度视频 | 四虎5151久久欧美毛片 | 色鬼综合网 | 久久精品日本啪啪涩涩 | 亚洲国产精选 | 夜夜爽88888免费视频4848 | 天天操天天插 | 久久av免费电影 | 欧美午夜一区二区福利视频 | 精品国产成人在线 | 国产成人精品在线观看 | 国产免费三级在线观看 | 在线国产精品视频 | 日日激情| 精品一区二区在线免费观看 | 亚洲午夜久久久久久久久 | 97在线公开视频 | 久久夜视频 | 激情五月在线观看 | 91资源在线 | 亚洲免费在线观看视频 | 最近最新最好看中文视频 | 综合久久久 | 国产精品18久久久久久久久 | 国产精品理论片在线观看 | 欧美日产一区 | 欧美日韩激情网 | 九九激情视频 | 国产美女精品视频免费观看 | 国产 成人 久久 | 日韩欧美一区二区不卡 | 国产成人精品综合久久久 | 麻豆视传媒官网免费观看 | 免费黄色激情视频 | 五月天综合网站 | 国产中文字幕在线播放 | 精品影院一区二区久久久 | av久久在线 | 国产人成精品一区二区三 | 中文字幕电影在线 | 91亚洲精品久久久蜜桃 | 国产成人免费在线观看 | 99精品视频网站 | 国产亲近乱来精品 | 日韩亚洲国产中文字幕 | 综合色影院 | 国产精在线 | 国产精品一区二区在线播放 | 国产美女视频免费观看的网站 | 亚洲国产日本 | www.色就是色 | 丁香综合av | 天天干夜夜夜 | 欧美日韩在线视频一区二区 | 日韩a在线观看 | 久草热久草视频 | 色天天中文 | 69av视频在线观看 | 亚洲九九九在线观看 | 久久人人97超碰国产公开结果 | 色婷婷国产在线 | 久草在线综合网 |