日韩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 数据访问策略 (转)的全部內容,希望文章能夠幫你解決所遇到的問題。

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

日韩欧美在线视频一区二区三区 | 99精品免费在线观看 | 国产一级片免费视频 | 亚洲国产精品影院 | 久久电影国产免费久久电影 | www·22com天天操 | av天天色 | 丁香婷婷在线 | 亚洲无吗视频在线 | 国产91国语对白在线 | 亚洲在线视频免费观看 | 日本中文字幕网址 | 亚洲精品视频免费在线观看 | 在线观看午夜av | 国产a国产 | www操操操| 国产精品丝袜 | 久久精品日本啪啪涩涩 | 波多野结衣视频一区二区 | 国内精品中文字幕 | 又湿又紧又大又爽a视频国产 | 欧美日韩99 | 久久精品99国产 | 美女免费电影 | 69亚洲乱| 久久国产一区二区 | 国产精品一级在线 | 亚洲爱爱视频 | 欧美 日韩 久久 | 日本最大色倩网站www | 91丨porny丨九色| 亚洲成人第一区 | 国产精品久久久久久妇 | 永久免费毛片在线观看 | 夜色在线资源 | 一级淫片在线观看 | 亚州欧美精品 | 亚洲精品午夜aaa久久久 | 色姑娘综合 | 韩日av在线 | 在线免费亚洲 | 中文字幕黄色网 | 婷婷色六月天 | 在线观看av黄色 | 国产精品久久久久久久毛片 | 精品国产一区在线观看 | 国产区欧美 | 国产福利91精品 | 久草在线最新 | 久久亚洲免费视频 | 狠狠色丁香久久婷婷综合_中 | 精品久久久久久久久久久久 | 99超碰在线播放 | 精品美女视频 | 91porny九色91啦中文 | 九九免费在线观看 | 国产精品久久久久久久电影 | 日韩中文字幕免费视频 | www.黄色在线| 国内精品亚洲 | 中文字幕在线观看播放 | 欧美精品一区在线发布 | 日韩精品一区二区三区中文字幕 | 亚洲精品国产日韩 | 天天操天天摸天天干 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 欧美日韩国产一区二区在线观看 | 免费在线观看一级片 | 日韩手机在线 | 亚洲免费公开视频 | 中文字幕色在线 | 在线国产视频一区 | 中文在线中文资源 | 色99在线 | 在线导航av | 91九色视频网站 | 亚洲女同ⅹxx女同tv | 久草91视频 | 久久96国产精品久久99漫画 | 中文字幕在线日本 | 五月婷婷综合久久 | 狠狠久久综合 | 欧美一级电影在线观看 | 国产精品久久久av久久久 | 精品国产乱码一区二区三区在线 | av高清一区 | 中文字幕无吗 | 成年人在线观看网站 | 高清在线一区二区 | 九九精品视频在线看 | 嫩草av在线 | av再线观看 | 亚洲三级毛片 | 久久综合国产伦精品免费 | 国产女人40精品一区毛片视频 | 欧美一区二区三区在线看 | 欧美国产日韩一区二区三区 | 波多野结衣在线播放一区 | 亚洲日本va在线观看 | 91黄在线看 | 美女视频久久久 | 国产在线一线 | 91成品人影院 | 午夜黄色影院 | 99视频+国产日韩欧美 | 久久国产麻豆 | 欧美一二区视频 | 久久久久久久久久久国产精品 | 91黄色小网站 | 国产色女人 | 欧美色操| 最新av网址在线 | 久久久久久不卡 | 免费精品视频在线观看 | 日韩一区二区三区免费视频 | 亚洲精品视频在线观看免费视频 | 亚洲免费视频在线观看 | 伊人婷婷色 | 69精品在线观看 | 久久涩视频 | 精品999 | 日韩精品中文字幕在线 | 91伊人久久大香线蕉蜜芽人口 | 999抗病毒口服液 | 国产成人三级三级三级97 | 欧美日韩二区三区 | 久久精品永久免费 | 精品视频免费在线 | 色在线国产 | 日本最大色倩网站www | 成年人免费在线播放 | 日本天天操 | 国产精品av一区二区 | 五月婷婷六月丁香在线观看 | 2019中文字幕网站 | 最近av在线| 亚洲首页 | 国产日韩精品在线 | www日日 | 亚洲色图激情文学 | 久久精品99久久久久久2456 | 狠狠色伊人亚洲综合网站色 | 亚洲最新av网址 | 黄污视频大全 | 黄色软件在线观看免费 | 亚洲精品中文在线 | 亚洲免费公开视频 | 国产伦理久久 | 日韩激情影院 | 奇米影视四色8888 | 欧美a级片免费看 | 日本中文字幕视频 | 中文字幕精品三区 | 午夜色大片在线观看 | 在线黄色av | 色播激情五月 | 成人在线视频你懂的 | 国产99在线播放 | 99这里只有精品视频 | 18国产精品福利片久久婷 | 成年人视频在线观看免费 | 久久av网址| av在线免费观看黄 | 人人草人 | 久久伊人精品天天 | 草久热| 免费在线一区二区三区 | 亚洲2019精品 | 丁香导航| 在线观看网站黄 | 亚洲日本欧美在线 | 中文字幕网站 | 精品久久久久久亚洲综合网站 | 狠狠狠干狠狠 | 五月婷网站 | 日本二区三区在线 | 激情五月av | 久久久久久久久毛片 | 日韩欧美在线视频一区二区三区 | 69av国产 | 亚洲国产网站 | 色婷婷99| 新版资源中文在线观看 | 日日操操操 | 午夜在线观看一区 | 亚洲黄色av网址 | 国产亚洲精品久久久网站好莱 | 日日干天天 | 国产老熟 | 欧美成人视| 探花在线观看 | 久久av影视 | 亚洲黄色激情小说 | 国产黄色免费在线观看 | 免费看的黄网站 | 911av视频| 国产无遮挡猛进猛出免费软件 | 色偷偷88888欧美精品久久久 | 伊人色**天天综合婷婷 | 欧美日韩激情视频8区 | 97在线观看视频免费 | 亚洲人精品午夜 | 蜜桃av综合网 | 日韩高清精品免费观看 | 国产又粗又猛又色又黄视频 | 成人在线观看免费 | av888.com| 丁香五月网久久综合 | 在线精品视频在线观看高清 | 69国产盗摄一区二区三区五区 | 日韩高清av| 91精品一区二区三区蜜臀 | 免费成人在线观看 | 日精品 | 国产丝袜| 黄色录像av | 国产资源在线观看 | 麻豆成人精品视频 | 亚洲区视频在线 | 国产美女视频网站 | 亚洲精品美女在线 | 亚洲免费公开视频 | 丰满少妇在线观看资源站 | 久久久国产影院 | 免费观看完整版无人区 | 青青网视频 | 欧美激情视频一区 | 国产精品九九热 | 国产不卡av在线 | 久久精品观看 | 欧美日韩精品在线播放 | 中文字幕在线播出 | 亚洲最新av在线网址 | 97精品国产97久久久久久粉红 | 免费av电影网站 | 婷婷网站天天婷婷网站 | 国产免费国产 | 日韩高清免费无专码区 | 91免费网站在线观看 | 久久久精品国产一区二区电影四季 | 亚洲视频2 | 欧美日韩国产成人 | 欧美亚洲另类在线视频 | 最近中文字幕免费视频 | a级片韩国 | 久久久夜色 | 福利一区在线视频 | 久久综合狠狠狠色97 | adn—256中文在线观看 | 欧美少妇18p| 日韩在线观看的 | 在线免费av网站 | 亚洲激情一区二区三区 | 久久久影视 | 99视频免费播放 | 99国产情侣在线播放 | 99精品免费视频 | 国产精品国产三级国产不产一地 | 久久久在线免费观看 | 97在线超碰 | 精品久久网 | 欧美激情综合色 | 久草视频观看 | 久久国产精品系列 | 久久综合中文字幕 | 美女黄频免费 | 手机看片1042 | 国产高清在线观看 | 在线激情小视频 | 激情亚洲综合在线 | 国产成人精品一区二区三区福利 | 日韩视频区 | 91精品视频免费在线观看 | 色av资源网 | 亚洲精品在线播放视频 | 成人av电影免费观看 | 激情 亚洲| 久久精品视频网站 | 黄色av免费电影 | 国产精品久久久久久久久久白浆 | 欧美一区中文字幕 | 一 级 黄 色 片免费看的 | 日韩精品久久一区二区三区 | 国产成人a亚洲精品 | 久久99久久99精品免费看小说 | 国产免费人成xvideos视频 | 成人午夜影院 | 99免费观看视频 | 色无五月 | 91成人看片| 在线欧美国产 | 91香蕉嫩草 | 国产精品久久久久久久免费 | 97国产超碰 | 成人性生活大片 | 黄色软件在线看 | 欧美日韩一级视频 | 在线看黄色av | 欧美国产不卡 | 五月婷婷综合在线视频 | 天海冀一区二区三区 | 亚洲黄色精品 | 人人操日日干 | 免费污片 | 国产精品一区二区久久精品 | 黄色成人91| 亚洲综合色播 | 色综合天天色综合 | 亚洲理论在线观看电影 | 久久在线精品视频 | 久久免费视频国产 | 亚洲免费精品一区二区 | www.天天色.com | 亚洲国产欧美一区二区三区丁香婷 | 成人黄色短片 | 成 人 黄 色 视频免费播放 | 日本三级久久 | aaawww| 狠狠的干狠狠的操 | 亚洲激情五月 | 成人av在线影视 | 深夜成人av | 国产精品a级 | 欧美另类交人妖 | 久久精品99国产精品亚洲最刺激 | 日韩免费观看一区二区 | 久久超碰99 | 人人超碰人人 | 一区二区三区免费在线观看 | 天天干天天射天天插 | 精品国产一区二区三区日日嗨 | 五月婷丁香网 | 国产成人久久av免费高清密臂 | 欧美天堂视频在线 | 香蕉视频在线免费 | 久草国产在线 | 国产黄色高清 | 国产区 在线| 超碰人人草 | 亚洲综合成人av | 嫩小bbbb摸bbb摸bbb | av在线a| 一区二区三区四区不卡 | 黄色av电影在线 | 久久看片网| 国产美女视频免费观看的网站 | 亚洲第二色 | 看全黄大色黄大片 | 国产一级高清视频 | 亚洲精品视频在线观看视频 | 久久久免费国产 | 国产一区二区免费在线观看 | 国产精品视频免费在线观看 | 免费a级毛片在线看 | www国产精品com | 久要激情网| 精品视频免费播放 | 7799av| 国产精品免费一区二区三区在线观看 | 91精品国产三级a在线观看 | 天天综合精品 | 久久亚洲热| 91在线看视频免费 | 欧美激情视频一区 | 玖玖视频精品 | 天天色天天操综合 | 成人精品一区二区三区中文字幕 | 国产成人精品电影久久久 | 亚洲免费精品一区二区 | 亚洲成人av在线播放 | 久精品视频在线 | 天天干天天干天天操 | 欧美性精品 | 久草在线精品观看 | 久久精品爱爱视频 | 又长又大又黑又粗欧美 | 国产成人一二三 | 亚洲欧美怡红院 | 韩国av永久免费 | 69中文字幕| 亚洲精品在线观看av | 波多野结衣视频在线 | 黄色软件网站在线观看 | 在线色网站 | 国产成人精品三级 | 国产人免费人成免费视频 | 久久久国产电影 | 午夜精品久久久久久久久久久 | 91最新网址在线观看 | 欧美在线视频免费 | 日日夜夜免费精品视频 | 国产福利在线不卡 | 久精品视频在线观看 | 国产最新视频在线观看 | 天天操夜操 | 午夜精品久久久久久久久久久久 | 五月婷婷免费 | 美女视频黄网站 | 狠狠干夜夜操 | x99av成人免费 | 亚洲激情一区二区三区 | 麻豆影视在线免费观看 | 在线视频91 | 成人动图 | 亚洲第一中文网 | 欧美a级免费视频 | 国产精品成人一区二区三区吃奶 | 国产资源免费 | 国产成人精品一区二区三区福利 | 2023国产精品自产拍在线观看 | 99精品视频在线 | 久久伊人精品天天 | 久久免费精品一区二区三区 | 中文在线亚洲 | 午夜99| 中文字幕在线一二 | 日日操天天射 | 精品 一区 在线 | 99视频免费看 | 91最新地址永久入口 | 丁香婷婷综合激情 | 国产 在线观看 | 青草视频在线 | 中日韩男男gay无套 日韩精品一区二区三区高清免费 | 日韩欧美成 | 日韩精品一区二区三区不卡 | 91免费版成人 | 日韩免费电影 | 色偷偷88888欧美精品久久久 | 久热久草在线 | 中文字幕在线播放视频 | 成年人免费在线播放 | 在线观看中文字幕第一页 | 综合伊人av | 麻豆视频91 | 综合婷婷久久 | 婷婷丁香激情综合 | 免费看色网站 | 欧美日韩三区二区 | 久操视频在线观看 | 在线精品播放 | 久草网站 | 日韩v在线91成人自拍 | 激情电影影院 | 色婷婷综合久久久久中文字幕1 | 91干干干 | 日韩在线高清 | 亚洲精品在线视频网站 | 中文字幕第一页在线播放 | 国产一级高清 | av色网站| 二区三区精品 | 欧美性极品xxxx做受 | av永久网址 | 久草在线视频国产 | 国产一区成人在线 | 日韩免费福利 | 国内精品久久久 | 久久免费av电影 | 久久亚洲二区 | 天天躁日日躁狠狠躁av麻豆 | 97精品超碰一区二区三区 | 成人av资源网 | 一区在线播放 | 国产精品私拍 | www.狠狠操.com| 亚洲激情在线观看 | 国产精品久久久久av福利动漫 | 日韩欧美一区二区三区免费观看 | 99精品视频在线播放免费 | 91成人精品一区在线播放69 | 欧美a级在线 | 九九视频在线观看视频6 | 97成人精品视频在线观看 | 久草精品视频 | 成人一级在线观看 | 欧美黑人猛交 | 免费av试看| 欧美aaa一级 | 91麻豆精品国产91 | 亚洲专区欧美专区 | 狠狠干天天 | 五月色综合 | 韩日在线一区 | 日韩超碰 | 亚洲天堂网在线视频观看 | 久久五月网| 狠狠干夜夜操 | 啪一啪在线 | 久久久久久免费视频 | 国产123av | 婷婷综合电影 | 99久久日韩精品视频免费在线观看 | www.日本色 | 久久免费视频网 | 国产精品自拍在线 | 西西人体4444www高清视频 | 97在线免费视频观看 | 久久久久 免费视频 | 成人精品999 | 在线观看亚洲精品 | 午夜久久久影院 | 97国产情侣爱久久免费观看 | 午夜少妇av | 免费三级骚 | 国产日韩在线视频 | 91成年人在线观看 | 国产精品24小时在线观看 | 91九色蝌蚪视频网站 | 97超碰在线免费观看 | 婷婷在线不卡 | 97手机电影网 | 91麻豆精品国产91久久久无限制版 | 开心激情婷婷 | 精品美女视频 | 一区二区三区精品在线视频 | 91精品国产乱码 | 在线观看免费视频 | 精品美女久久 | 在线性视频日韩欧美 | 涩涩网站在线观看 | 天天艹天天 | 国产成人久久精品77777综合 | 国产精品久久久久一区二区三区共 | 国产人免费人成免费视频 | 亚洲一区二区三区毛片 | 国产日韩欧美在线 | 亚洲狠狠| 成人一区在线观看 | 久久免费国产视频 | 97超碰在 | 99人久久精品视频最新地址 | 亚洲最大av在线播放 | 国产在线色 | 国产九色91| 毛片在线播放网址 | av免费在线看网站 | 国产午夜三级一区二区三桃花影视 | jizz18欧美18| 久久成人在线视频 | 色伊人网 | 夜色在线资源 | 国产精品久久久久免费观看 | 中文字幕精品一区久久久久 | 婷婷av资源| 国产精品 国产精品 | 亚洲欧美成人综合 | 91精品国产乱码久久桃 | 人人爽人人爽人人片av | 怡红院成人在线 | 午夜精品三区 | 欧美成年人在线观看 | 亚洲欧美日韩国产精品一区午夜 | 中文字幕在线观看第一区 | 播五月婷婷 | 久久久久免费网站 | 欧美日韩精品在线视频 | 狠狠狠狠干 | .国产精品成人自产拍在线观看6 | 涩涩爱夜夜爱 | 九月婷婷色 | 麻豆小视频在线观看 | 日本黄色特级片 | 99久久国产免费,99久久国产免费大片 | 91九色视频在线播放 | 久久久久日本精品一区二区三区 | 中文伊人| 国内精品久久久久久中文字幕 | 亚洲精品国产综合99久久夜夜嗨 | 日韩精品一区电影 | 肉色欧美久久久久久久免费看 | 五月婷婷六月丁香 | 在线电影中文字幕 | 天天干天天拍天天操天天拍 | 天天性天天草 | 在线免费观看的av网站 | 最近中文字幕高清字幕在线视频 | 久久99亚洲精品久久久久 | 国产 亚洲 欧美 在线 | 911国产在线观看 | 中文在线免费观看 | 中文字幕免费观看 | 欧美一级片免费观看 | 日韩xxxxxxxxx | 亚州精品天堂中文字幕 | 射射射av | 久久电影国产免费久久电影 | 成人福利av| 亚洲国产99 | 91在线看| 国产成人精品999 | 日韩黄色在线观看 | 操操操夜夜操 | 久久国产精品一区二区三区四区 | 国产亚洲精品久久久久久移动网络 | 精品色999 | 久久欧美综合 | 日韩高清免费观看 | 精品av网站 | 久久久久电影网站 | 国产伦精品一区二区三区无广告 | 丁香婷婷激情啪啪 | 亚洲精品久久久蜜臀下载官网 | 欧美美女视频在线观看 | 国产精品资源在线观看 | 午夜精品视频免费在线观看 | 国产69精品久久久久久久久久 | 最近av在线| 99久久这里有精品 | 国产精品久久片 | 久久国产免费看 | 特级a老妇做爰全过程 | 午夜精品视频一区二区三区在线看 | 黄色av网站在线免费观看 | 综合久久久 | 亚洲综合欧美激情 | 一区二区三区四区免费视频 | 亚洲伊人天堂 | 日韩一级片网址 | 亚洲热视频 | 麻豆视频免费在线 | 亚洲国产69 | 久久久久久久久久久免费 | 九九精品毛片 | 五月婷激情 | 东方av免费在线观看 | 久久成人精品电影 | 97超级碰碰碰视频在线观看 | 一级片黄色片网站 | 四虎影视成人精品国库在线观看 | 国产涩涩在线观看 | 成人性生交大片免费观看网站 | 91精品1区2区 | 在线观看的av网站 | 国产精品成人在线观看 | 久久久首页 | 婷婷久月 | 欧美成人h版 | 最新国产在线观看 | 精品视频免费久久久看 | 欧美激情视频一区二区三区 | 国产精品99久久久久人中文网介绍 | 97人人超碰在线 | 国产不卡高清 | 日韩视频一区二区三区 | 国产玖玖精品视频 | 久草在线中文视频 | 日韩精品一区电影 | 国产色婷婷精品综合在线手机播放 | 成年人视频免费在线播放 | 人人操日日干 | 亚洲闷骚少妇在线观看网站 | 91精品啪在线观看国产81旧版 | 在线综合 亚洲 欧美在线视频 | 色综合天天做天天爱 | 日一日干一干 | 久久久久久久久久久久久9999 | 免费在线黄网 | 国产视频99 | 亚洲国产精品99久久久久久久久 | 欧美网站黄色 | 99久久婷婷 | 久久免费视频6 | 青青草视频精品 | av一级网站 | 日本精品视频在线观看 | 日韩福利在线观看 | 天天干天天射天天操 | 欧美一区二区三区在线播放 | 国产一级片直播 | 国产正在播放 | 四虎国产精品免费观看视频优播 | 国产视频精选 | 久草在线播放视频 | 亚洲一区二区视频 | 精品中文字幕在线观看 | 午夜精品视频在线 | 91九色在线观看 | 美女网色 | 日韩一区二区在线免费观看 | 日韩久久久久久久久久久久 | 国产专区在线 | 久久综合九色综合97_ 久久久 | 欧美黄网站 | 91激情视频在线播放 | 欧美日韩免费在线观看视频 | 国产福利精品一区二区 | 精品一区精品二区高清 | 国产黄色免费电影 | 亚洲精品www. | 日韩免费成人av | 日韩激情第一页 | av黄色大片 | 中文字幕在线观看视频免费 | 五月天高清欧美mv | 在线观看中文字幕一区二区 | 精品一区 精品二区 | 日韩黄色在线观看 | 国产精品永久在线观看 | 91精品久 | 亚洲最大成人免费网站 | 成人av电影免费在线观看 | 在线免费看黄网站 | 欧美性生活免费 | 黄色免费网站下载 | 国产精品成人品 | 在线免费看黄色 | 日韩欧美精品一区二区三区经典 | 伊人色综合久久天天网 | 91成人免费观看视频 | 日韩精品无 | 国产精品门事件 | 欧美成人aa | 天堂网一区二区三区 | 天天弄天天干 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 国产精品一区二区电影 | 婷婷日| 91香蕉视频 mp4 | 天堂av网在线 | 97视频在线观看成人 | 999色视频| 在线观看日韩免费视频 | 中文在线免费看视频 | 午夜国产福利在线观看 | 久久综合五月天婷婷伊人 | 91色蜜桃| 99热国产在线中文 | 在线久久| 五月婷婷六月丁香 | 亚洲国产成人精品在线 | 2021国产在线| 久久久人| 黄色福利视频网站 | 国产成人精品久久久 | 日本成人中文字幕在线观看 | 国产理论一区二区三区 | 91完整版在线观看 | 久久久国产一区 | 亚洲精品成人 | 一区二区三区四区精品视频 | 97精品国产aⅴ | 久久久999免费视频 日韩网站在线 | 日韩av一区二区在线 | 蜜桃视频在线视频 | 99精品视频在线看 | 久久9精品| 丁香激情婷婷 | 婷婷综合五月天 | 久久99精品国产麻豆宅宅 | 制服丝袜天堂 | 久久久精品欧美一区二区免费 | 在线成人高清电影 | 香蕉在线观看视频 | 婷婷综合久久 | 亚洲婷婷伊人 | 日韩免费一区二区在线观看 | 久久国产精品色av免费看 | 国产麻豆剧果冻传媒视频播放量 | 99色免费视频 | 区一区二区三区中文字幕 | 亚洲最大在线视频 | 国产精品免费久久久久久久久久中文 | 中文字幕在线观看资源 | 日韩精品久久一区二区三区 | 国产精品video| 综合精品在线 | 人人爽人人av | 欧美一级日韩免费不卡 | 亚洲精品综合在线观看 | 91传媒在线看 | av东方在线 | 国产高清视频在线免费观看 | 毛片一区二区 | 国产成人综合图片 | 日韩久久影院 | 久久久国产电影 | 香蕉97视频观看在线观看 | 国内综合精品午夜久久资源 | 亚洲国产精品日韩 | 中文在线8新资源库 | 久久99久| 精品国产一区二区三区在线观看 | 日韩黄色在线电影 | av短片在线观看 | 久草在线91 | 一区二区三区四区在线 | av在线播放亚洲 | 麻豆94tv免费版 | 一区二区三区在线观看中文字幕 | 欧美日韩高清一区 | 久久香蕉一区 | av中文字幕不卡 | 黄色软件网站在线观看 | 久久精品国产亚洲 | 亚洲精品乱码久久久久久蜜桃动漫 | 日本在线中文在线 | 国产精品久久久久久69 | 99精品久久只有精品 | av短片在线观看 | 欧美另类一二三四区 | 久久五月精品 | 久久爱导航 | 中文字幕在线观看第二页 | 亚洲三级在线 | 日韩视频免费播放 | 久草视频首页 | 91伊人影院 | 国产高清在线永久 | 国产原创av片| 日本韩国在线不卡 | 成人午夜电影在线观看 | 日批在线看 | 国产99久久久欧美黑人 | 久久精品7 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 在线最新av| 亚洲资源一区 | 日韩美女一级片 | 成人黄大片 | 人人艹人人 | 久久综合电影 | 成年人网站免费观看 | 欧美性精品 | av福利第一导航 | 日韩中文字幕a | 久久99精品久久久久久三级 | 91免费版在线观看 | 中文字幕91在线 | 欧美热久久| 婷婷在线视频观看 | 四虎5151久久欧美毛片 | 国内一级片在线观看 | 亚洲91中文字幕无线码三区 | 人人人爽 | 成人a视频 | 久草99| 国产美女免费观看 | 国产精品入口传媒 | 欧美日韩一区二区三区免费视频 | 精品亚洲国产视频 | 久久精品三级 | 在线国产激情视频 | 极品嫩模被强到高潮呻吟91 | 久久激情视频 久久 | 免费性网站 | 在线国产片 | 国产一级一级国产 | 中文字幕在线播放日韩 | 超碰国产在线观看 | 韩日电影在线 | 曰本免费av| 久久久久久久久久久久久久av | 99视频在线免费观看 | 国产精品嫩草影院9 | 在线观看视频国产一区 | 欧美最新大片在线看 | 一级免费看视频 | 国产手机视频精品 | 日韩高清一二区 | 日韩免费电影在线观看 | a色视频 | 69精品视频在线观看 | av一级片网站 | 国产专区在线看 | 精品主播网红福利资源观看 | 99精品视频免费 | 日韩精品不卡在线 | 久久久.com | 午夜视频福利 | 久久99爱视频 | 婷婷在线网| 国产三级国产精品国产专区50 | 免费下载高清毛片 | 国产精品中文 | 毛片久久久 | 国产一区二区精品91 | 欧美日韩高清在线观看 | 91污污| 中文字幕免费高 | av一区二区三区在线 | 二区三区在线视频 | 精品久久久久久亚洲 | 天天色视频 | 亚洲最新av在线网址 | 日韩成人黄色av | 91九色视频观看 | 一区二区三区精品久久久 | 日韩三级视频在线看 | 精品视频资源站 | 在线亚洲人成电影网站色www | 婷婷色中文网 | 欧美91视频 | 免费特级黄毛片 | 婷婷色5月 | 日韩v在线91成人自拍 | 999久久久久久久久6666 | 久久久受www免费人成 | 啪啪免费试看 | 日本护士三级少妇三级999 | 97视频在线免费 | 亚洲一区二区高潮无套美女 | 日本中出在线观看 | 91麻豆精品国产91久久久久久 | 亚洲精品电影在线 | 久久精品视频3 | 日韩欧美视频在线播放 | 国产一区电影在线观看 | 在线观看中文 | 日本精品va在线观看 | 不卡视频在线看 | 97国产大学生情侣白嫩酒店 | av色图天堂网 | 久久呀 | 久久久午夜精品理论片中文字幕 | 2019久久精品 | 国产96精品| 爱色婷婷 | 欧美a级片免费看 | 久久久久久久久久久久影院 | av在线在线| 在线观看免费黄视频 | 久久国产精品99久久久久久老狼 | 又黄又爽又无遮挡免费的网站 | 激情综合五月天 | 在线观看你懂的网址 | 97超碰在线久草超碰在线观看 | 国产福利电影网址 | 国产真实在线 | 黄色一级免费电影 | 99久久久国产精品美女 | 久久精品99国产精品亚洲最刺激 | 在线精品一区二区 | 九九综合九九 | 亚洲在线视频免费 | 午夜美女福利 | 色婷婷综合久久久久中文字幕1 | 五月婷婷色综合 | 国产精品久久久毛片 | 最新成人在线 | 国语黄色片 | 久久香蕉电影网 | 韩日精品在线 | 黄色国产成人 | 91免费视频网站在线观看 | 日韩午夜一级片 | 99国产视频 | 中文字幕在线观看视频一区 | 免费看一级特黄a大片 | 久久久一本精品99久久精品 | 在线观看国产一区二区 | 国产亚洲视频在线观看 | 精品五月天 | av免费高清观看 | 激情网五月天 | 久久视影| 国产一区二区中文字幕 | 色五婷婷| 国产精品免费av | 在线观看免费版高清版 | 一区二区三区四区五区六区 | 超碰在线人人爱 | 五月婷婷视频在线观看 | 国产精品私拍 | 亚洲精品成人av在线 | 日韩高清在线不卡 | 国产一级视屏 | 丁香婷婷激情五月 | 国产麻豆电影在线观看 | 久久精彩免费视频 | 91 中文字幕| 99视频在线看 | 久久不射电影院 | 国产亚洲高清视频 | 99精品在线播放 | 91视频 - v11av | 在线观看一级视频 | 国产日产精品一区二区三区四区的观看方式 | 日本中文字幕网 | 91久久久久久久 | 国产精品久久久久久久久久三级 | 人人爱爱人人 | 欧美xxxx性xxxxx高清 | 色人久久| 开心激情网五月天 | 日韩免费在线观看 | 成人手机在线视频 | 在线观看国产www | 波多野结衣网址 | 美女久久久 | 色网站视频 | 91精品亚洲影视在线观看 | av免费在线观看1 | 天天射天天干天天 | 国产精品国产三级国产aⅴ无密码 | 黄色片网站 | 伊人亚洲综合网 | 中文字幕一区三区 |