ASP.NET编程:计划.NET使用程序数据会见层五年夜准绳
今天去面试,被问到C#中的new关键字,看了那么多的书对new关键字还是有一定认识,回来又把new复习了一遍,发现了许多以前还不知道的细节。程序|会见|计划|数据 择要:年夜多半利用.NET框架组件事情的开辟职员的一个中心事情是完成数据会见功效,他们创建的数据会见层(dataaccesslayer)是使用程序的精髓部分。本文概述了利用VisualStudio.NET和.NET框架组件创建数据会见层必要思索的五个设法。这些技能包含经由过程利用基类(baseclass)使用面绝对象手艺和.NET框架组件基本布局,使类简单承继,在决意显现办法和内部界眼前细心地查验需求。假如你正在创建以数据为中央(data-centric)的.NET框架组件使用程序,你终极必需创建数据会见层。大概你晓得在.NET框架组件中创建本人的代码有良多优点。由于它撑持完成和接口(interface)承继,你的代码更简单反复利用,出格是被利用分歧的框架组件兼容(Framework-compliant)言语的开辟职员利用。本文我将概述为基于.NET框架组件的使用程序创建数据会见层的五条划定规矩。
入手下手前,我必需提示你创建的任何基于本文会商的划定规矩的数据会见层必需与传统Windows平台上开辟职员喜好的多层大概n层使用程序兼容。在这类布局中,体现层包括Web窗体、Windows窗体、挪用与数据会见层的事情响应的事件层的XML服务代码。该层由多个数据会见类(dataaccessclasse)构成。换句话说,在事件处置和谐不是需要的情形下,体现层将间接挪用数据会见层。这类布局是传统的模子-视列表-把持程序(Model-View-Controller,MVC)形式的变体,在多种情形下被VisualStudio.NET和它表露的控件接纳。
划定规矩1:利用面向工具特征
最基础的面向工具事件是创建一个利用完成承继的笼统类。这个基类能够包含你的一切数据会见类经由过程承继可以利用的服务。假如那些服务充足了,它们就可以经由过程在全部构造的基类散布完成反复利用。比方最复杂的情形是基类可以为衍生类处置毗连的创建历程,如列表1所示。
ImportsSystem.Data.SqlClient
NamespaceACME.Data
PublicMustInheritClassDALBase:ImplementsIDisposable
Private_connectionAsSqlConnection
ProtectedSubNew(ByValconnectAsString)
_connection=NewSqlConnection(connect)
EndSub
ProtectedReadOnlyPropertyConnection()AsSqlConnection
Get
Return_connection
EndGet
EndProperty
PublicSubDispose()ImplementsIDisposable.Dispose
_connection.Dispose()
EndSub
EndClass
EndNamespace
列表1.复杂基类
在列表中能够看到,对DALBase类作了MustInherit标志(C#中的笼统),以确保它在承继干系中利用。接着该类在大众机关函数中包含了一个实例化的公有SqlConnection工具,它吸收毗连字符串作为一个参数。当来自IDisposable接口的Dispose办法确保毗连工具已被设置了的时分,受回护的(protected)Connection属性同意衍生类会见该毗连工具。
即便鄙人面简化的例子中你也能入手下手看到笼统基类的用途:
PublicClassWebData:InheritsDALBase
PublicSubNew()
MyBase.New(ConfigurationSettings.AppSettings("ConnectString"))
EndSub
PublicFunctionGetOrders()AsDataSet
DimdaAsNewSqlDataAdapter("usp_GetOrders",Me.Connection)
da.SelectCommand.CommandType=CommandType.StoredProcedure
DimdsAsNewDataSet()
da.Fill(ds)
Returnds
EndFunction
EndClass
在这类情形下,WebData类承继自DALBase,了局就是不用忧虑实例化SqlConnection工具,而是经由过程MyBase关头字(大概C#中的基关头字)复杂地把毗连字符串传送给基类。WebData类的GetOrders办法能利用Me.Connection(在C#中是this.Connection)会见受回护的属性。固然这个例子绝对复杂,可是你将在划定规矩2和3中看到基类也供应了别的的服务。
当数据会见层必需在COM+情况中运转时笼统的基类很有效。在这类情形下,由于同意组件利用COM+的需要代码庞大很多,以是更好的体例是创建一个如列表2所示的服务组件(servicedcomponent)基类。
Transaction(TransactionOption.Supported),_
EventTrackingEnabled(True)>_
PublicMustInheritClassDALServicedBase:InheritsServicedComponent
Private_connectionAsSqlConnection
ProtectedOverridesSubConstruct(ByValsAsString)
_connection=NewSqlConnection(s)
EndSub
ProtectedReadOnlyPropertyConnection()AsSqlConnection
Get
Return_connection
EndGet
EndProperty
EndClass
列表2.服务组件基类
在这段代码中,DALServicedBase类包括的基础功效与列表1中的不异,可是加上了从System.EnterpriseServices名字空间的ServicedComponent的承继,而且包含了一些属性,指明组件撑持工具机关、事件和静态跟踪。接着该基类细心地捕获组件服务办理器(ComponentServicesManager)中的机关字符串而且再次创建和表露SqlConnection工具。我们要注重的是当一个类承继自DALServicedBase时,它也承继了属性的设置。换句话说,一个衍生类的事件选项也设置为Supported。假如衍生类想重载这类举动,它能在类的条理从头界说该属性。
别的,衍生类在得当情形下应当有益于本身重载和共享办法。利用重载的办法(一个办法有多个挪用旌旗灯号)在实质上有两种情形。起首,它们在一个办法必要承受多品种型的参数时利用。框架组件中的典范例子是System.Convert类的办法。比方ToString办法包括18个承受一个参数的重载办法,每一个重载办法的范例分歧。其次,重载的办法用于表露参数数目不休增加的旌旗灯号,而不是分歧范例的需要参数。在数据会见层中这类重载变得效力很高,由于它能用于为数据检索和修正表露瓜代的旌旗灯号。比方GetOrders办法能够重载,如许一个旌旗灯号不承受参数并前往一切定单,可是附加的旌旗灯号承受参数以标明挪用程序但愿检索特定的主顾定单,代码以下:
PublicOverloadsFunctionGetOrders()AsDataSet
PublicOverloadsFunctionGetOrders(ByValcustomerIdAsInteger)AsDataSet
这类情形下的一个好的完成技能是笼统GetOrders办法的功效到一个能被每一个重载旌旗灯号挪用的公有的大概受回护的办法中。
共享办法(C#中的静态办法)也能用于表露数据会见类的一切实例可以会见的字段、属性和办法。只管共享成员不克不及与利用组件服务(ComponentServices)的类一同利用,可是关于在数据会见类的共享机关函数中检索并被一切实例读取的只读数据是有效的。利用共享成员读/写数据时要当心,由于为了会见该共享数据,实行的多个线程大概会合作。
划定规矩2:保持计划引导
随VisualStudio.NET一同公布的在线文档中有一个叫"类库开辟职员的计划引导(DesignGuidelinesforClassLibraryDevelopers)"的主题,它掩盖了类、属性和办法的名字转换,是重载的成员、机关函数和事务的增补形式。你必需遵守名字转换的次要缘故原由之一是.NET框架组件供应的跨言语(cross-language)承继。假如你在VisualBasic.NET中创建一个数据会见层基类,你想确保利用.NET框架组件兼容的别的言语的开辟职员能承继它并简单了解它如何事情。经由过程保持我概述的引导目标,你的名字转换和机关就不会是言语特定的(languagespecific)。比方,你大概注重到在本文例子的代码中第一个词小写,并加上intercaps是用于办法的参数的,每一个词年夜写是用于办法的,基类利用Base标记来标识它是一个笼统类。
能够推想.NET框架组件计划引导都是一般计划形式,像GangofFour(Addison-Wesley,1995)写的DesignPatterns纪录的一样。比方.NET框架组件利用了Observer形式的一个变体,叫做Event形式,在类中表露事务时你必需遵守它。
划定规矩3:使用基本布局(Infrastructure)
.NET框架组件包含一些类和机关,它们能帮助处置一般的与基本布局相干的事件,比方安装和非常处置。经由过程基类把这些观点与承继组合起来将十分壮大。比方,你能思索一下System.Diagnostics名字空间中表露的跟踪功效。除供应Trace和Debug类外,该名字空间还包含衍生自Switch和TraceListener的类。Switch类的BooleanSwitch和TraceSwitch能被设置用于翻开和封闭使用程序和设置文件,在TraceSwitch中能够表露多条理跟踪。TraceListener类的TextWriterTraceListener和EventLogTraceListener分离将Trace和Debug办法的输出定位到文本文件和事务日记。
如许作的了局是给基类增加了跟踪功效,使衍生类纪录动静日记更复杂。接着使用程序能利用设置文件把持是不是同意跟踪。你能包含一个BooleanSwitch范例的公有变量并在机关函数中实例化它来给列表1中的DALBase增加这个功效:
PublicSubNew(ByValconnectAsString)
_connection=NewSqlConnection(connect)
_dalSwitch=NewBooleanSwitch("DAL","DataAccessCode")
EndSub
传送给BooleanSwitch的参数包含名字和形貌。接着你能增加一个受回护的属性翻开和封闭开关,也能增加一个属性利用Trace工具的WriteLineIf办法格局化并写进跟踪动静:
ProtectedPropertyTracingEnabled()AsBoolean
Get
Return_dalSwitch.Enabled
EndGet
Set(ByValValueAsBoolean)
_dalSwitch.Enabled=Value
EndSet
EndProperty
ProtectedSubWriteTrace(ByValmessageAsString)
Trace.WriteLineIf(Me.TracingEnabled,Now&":"&message)
EndSub
经由过程这类路子,衍生类本人其实不晓得开关(switch)和监听(listener)类,当数据会见类发生一个成心义的旌旗灯号时可以复杂地挪用WriteTrace办法。
type="System.Diagnostics.TextWriterTraceListener"
initializeData="DALLog.txt"/>
列表3.跟踪的设置文件
为了创建一个监听器并翻开它,必要利用使用程序设置文件。列表3显现了一个复杂的设置文件,它可以翻开方才显现的数据会见类开关,并经由过程myListener挪用TextWriterTraceListener把输入定位到文件DALLog.txt中。固然,你能经由过程从TraceListener类衍生程序化地创建监听器并把该监听器间接包括在数据会见类中。
PublicClassDALException:InheritsApplicationException
PublicSubNew()
MyBase.New()
EndSub
PublicSubNew(ByValmessageAsString)
MyBase.New(message)
EndSub
PublicSubNew(ByValmessageAsString,ByValinnerExceptionAs
Exception)
MyBase.New(message,innerException)
EndSub
在这儿增加自界说成员
PublicConnectStringAsString
EndClass
列表4.自界说非常类
你从中收益的第二个基本布局是布局化非常处置(SEH)。在最基础的条理,数据会见类可以表露它的衍生自System.ApplicationException的Exception(非常)工具并能进一步表露自界说成员。比方,列表4中显现的DALException工具能用于包装数据会见类中的代码发生的非常。接着基类能表露一个受回护的办法包装该非常,组装自界说成员,并把它发还给挪用程序,以下所示:
ProtectedSubThrowDALException(ByValmessageAsString,_
ByValinnerExceptionAsException)
DimnewMineAsNewDALException(message,innerException)
newMine.ConnectString=Me.Connection.ConnectionString
Me.WriteTrace(message&"{"&innerException.Message&"}")
ThrownewMine
EndSub
利用这类办法,衍生类能复杂地挪用受回护的办法,传送出来一个特定的数据非常(典范的有SqlException大概OleDbException),该非常被截取并增加了附属于特定命据域的动静。基类在DALException中包装该非常并把它发还到挪用程序。这就同意挪用程序用一个Catch语句容易地捕获一切来自数据会见类的非常。
作为选择之一,你能够看一看MSDN上公布的"ExceptionManagementApplicationBlockOverview"。该框架组件经由过程一系列工具分离了非常和使用程序日记纪录。实践上,经由过程从.NET框架组件供应的BaseApplicationException类衍生的自界说非常类可以复杂地拔出该框架组件。
划定规矩4:细心选择内部界面
在你计划数据会见类的办法时,必要思索它们如何承受和前往数据。对年夜多半开辟职员来讲,次要有三个选择:间接利用ADO.NET工具、利用XML、利用自界说类。
假如间接表露ADO.NET工具,你能利用一到两个编程模子。第一个包含数据集和数据表工具,它们对不毗连数据会见很有效。有良多关于数据集和与它联系关系的数据表的文章,可是当你必需利用从上层数据存储断开的数据时它才最有效处。换句话说,数据集能在使用程序各层之间传送,即便那些层在物理上是散布式的,当营业和数据服务层安排在统一群服务器上而且与体现服务分隔时也能利用。别的,数据集工具是经由过程基于XML的Web服务前往数据的幻想办法,由于它们是可串行化的,因而能在SOAP回应动静中前往。
这与利用完成IDataReader接口的类(比方SqlDataReader和OleDbDataReader)会见数据分歧。数据浏览器(datareader)用只向前的,只读的体例会见数据。二者之间最年夜的分歧是数据集和数据表工具能在使用程序域之间传送,经由过程传送值(byvalue)完成,但是数据浏览器能在遍地传送,可是一样平常经由过程援用(byreference)完成。在列表5中,Read和GetValues在服务器过程当中实行而且它们的前往值复制到客户端。
该图显现了数据浏览器如何存活在使用程序域中,它在那儿它被创建,而且对它的一切会见了局都在客户端和服务器使用程序域之间的轮回当中。这意味着当数据会见办法在不异的使用程序域运转时,应当前往数据浏览器作为挪用者。
利用数据浏览器时有两个成绩必要思索。起首,当你从数据会见类的一个办法前往数据浏览器时,你必需思索与数据浏览器联系关系的毗连工具的保存期。默许情形是当挪用程序经由过程数据浏览重视复时毗连仍旧是忙的,不幸的是当挪用程序停止后,毗连仍旧翻开,因而它不前往到毗连池(假如同意毗连池)。可是,当经由过程传送CommandBehavior.CloseConnection列举给command工具的ExecuteReader办法,毗连的Close办法被挪用时,你能命令数据浏览器封闭它的毗连。
其次,为了把体现层从特定的框架组件数据供应程序(比方SqlClient大概OleDb)平分离出来,挪用代码应当利用IDataReader接口(比方SqlDataReader)而不是详细范例来援用前往值。经由过程这类办法,假如使用程序后端从Oracle移植到SQLServer,大概数据会见类的一个办法的前往范例改动了,体现层也不必要变动。
假如你但愿数据会见类前往XML,你能够从System.Xml名字空间中的XmlDocument和XmlReader当选择一个,它与数据集和IDataReader相似。换句话说,当数据从数据源断开时你的办法应当前往一个XmlDocument(大概XmlDataDocument),但是XmlReader可用于会见XML数据的流。
最初,你也能决意与大众属性一同前往自界说类。这些类可使用Serialization(串行化)属性标志,如许它们就可以超过使用程序域复制。别的,假如你从办法中前往多个工具,就必要强化范例(stronglytyped)的汇合类。
ImportsSystem.Xml.Serialization
_
PublicClassBook:ImplementsIComparable
PublicProductIDAsInteger
PublicISBNAsString
PublicTitleAsString
PublicAuthorAsString
PublicUnitCostAsDecimal
PublicDescriptionAsString
PublicPubDateAsDate
PublicFunctionCompareTo(ByValoAsObject)AsInteger_
ImplementsIComparable.CompareTo
DimbAsBook=CType(o,Book)
ReturnMe.Title.CompareTo(b.Title)
EndFunction
EndClass
PublicNotInheritableClassBookCollection:InheritsArrayList
DefaultPublicShadowsPropertyItem(ByValproductIdAsInteger)_
AsBook
Get
ReturnMe(IndexOf(productId))
EndGet
Set(ByValValueAsBook)
Me(IndexOf(productId))=Value
EndSet
EndProperty
PublicOverloadsFunctionContains(ByValproductIdAsInteger)As_
Boolean
Return(-1IndexOf(productId))
EndFunction
PublicOverloadsFunctionIndexOf(ByValproductIdAsInteger)As_
Integer
DimindexAsInteger=0
DimitemAsBook
ForEachitemInMe
Ifitem.ProductID=productIdThen
Returnindex
EndIf
index=index+1
Next
Return-1
EndFunction
PublicOverloadsSubRemoveAt(ByValproductIdAsInteger)
RemoveAt(IndexOf(productId))
EndSub
PublicShadowsFunctionAdd(ByValvalueAsBook)AsInteger
ReturnMyBase.Add(value)
EndFunction
EndClass
列表6.利用自界说类
上列表(列表6)包括了一个复杂的Book类和与它联系关系的汇合类的例子。你能注重到Book类用Serializable做了标志,使它超过使用程序域能利用"byvalue"语法。该类完成了IComparable接口,因而当它包括在一个汇合类中的时分,默许情形下它将按Title排序。BookCollection类从System.Collections名字空间的ArrayList衍生,而且为了将该汇合限定到Book工具而埋没了Item属性和ADD办法。
经由过程利用自界说类你完整地把持了数据的体现、开辟职员的效力而且没有依附ADO.NET的挪用。可是这类路子必要更多的代码,由于.NET框架组件没有包括任何与工具相干的手艺映照。在这类情形下,你应当在数据会见类中创建一个数据读取器并利用它来组合自界说类。
划定规矩5:笼统.NET框架组件数据供应程序
最初一条划定规矩申明了为何和如何笼统数据会见类外部利用的.NET框架组件数据供应程序(dataprovider)。先前我说过ADO.NET编程模子表露了特定的.NET框架组件数据供应程序,包含SqlClient、OleDb和别的MSDNOnlineWeb站点上可用的。可是这类计划的了局是进步功能,为数据供应程序表露特定命据源功效的才能,它强制你决意利用那种数据供应程序编码。换句话说,开辟职员典范地会选择利用SqlClient或OleDb,接着在各自的名字空间间接对它们的类举行编程。
假如你想改动.NET框架组件数据供应程序,你必需从头编写数据会见办法。为了不这类情形产生,你可使用AbstractFactory计划形式。利用这类形式,你能创建一个复杂的类,它表露办法来创建次要的.NET框架组件数据供应程序工具(command、connection、dataadapter和parameter),而那些工具基于传送给机关函数的.NET框架组件数据供应程序的信息。列表7中的代码就是如许一个复杂的类。
publicenumProviderType:int{SqlClient=0,OLEDB=1}
publicclassProviderFactory{
publicProviderFactory(ProviderTypeprovider){
_pType=provider;
_initClass();
}
publicProviderFactory(){
_initClass();
}
privateProviderType_pType=ProviderType.SqlClient;
privatebool_pTypeSet=false;
privateType[]_conType,_comType,_parmType,_daType;
privatevoid_initClass(){
_conType=newType;
_comType=newType;
_parmType=newType;
_daType=newType;
//为供应程序初始化范例
_conType[(int)ProviderType.SqlClient]=typeof(SqlConnection);
_conType[(int)ProviderType.OLEDB]=typeof(OleDbConnection);
_comType[(int)ProviderType.SqlClient]=typeof(SqlCommand);
_comType[(int)ProviderType.OLEDB]=typeof(OleDbCommand);
_parmType[(int)ProviderType.SqlClient]=typeof(SqlParameter);
_parmType[(int)ProviderType.OLEDB]=typeof(OleDbParameter);
_daType[(int)ProviderType.SqlClient]=typeof(SqlDataAdapter);
_daType[(int)ProviderType.OLEDB]=typeof(OleDbDataAdapter);
}
publicProviderTypeProvider{
get{
return_pType;
}
set{
if(_pTypeSet){
thrownewReadOnlyException("Provideralreadysetto"
+_pType.ToString());
}
else{
_pType=value;
_pTypeSet=true;
}
}
}
publicIDataAdapterCreateDataAdapter(stringcommandText,IDbConnection
connection){
IDataAdapterd;
IDbDataAdapterda;
d=(IDataAdapter)Activator.CreateInstance(_daType[(int)_pType],
false);
da=(IDbDataAdapter)d;
da.SelectCommand=this.CreateCommand(commandText,connection);
returnd;}
publicIDataParameterCreateParameter(stringparamName,DbType
paramType){
IDataParameterp;
p=(IDataParameter)Activator.CreateInstance(_parmType[(int)_pType],
false);
p.ParameterName=paramName;
p.DbType=paramType;
returnp;
}
publicIDataParameterCreateParameter(stringparamName,DbType
paramType,Objectvalue){
IDataParameterp;
p=(IDataParameter)Activator.CreateInstance(_parmType[(int)_pType],
false);
p.ParameterName=paramName;
p.DbType=paramType;
p.Value=value;
returnp;
}
publicIDbConnectionCreateConnection(stringconnect){
IDbConnectionc;
c=(IDbConnection)Activator.CreateInstance(_conType[(int)_pType],
false);
c.ConnectionString=connect;
returnc;
}
publicIDbCommandCreateCommand(stringcmdText,IDbConnection
connection){
IDbCommandc;
c=(IDbCommand)Activator.CreateInstance(_comType[(int)_pType],
false);
c.CommandText=cmdText;
c.Connection=connection;
returnc;
}
}
列表7.ProviderFactory
为了利用该类,数据会见类的代码必需对多个.NET框架组件数据供应程序完成的接口(包含IDbCommand、IDbConnection、IDataAdapter和IDataParameter)举行编程。比方,为了利用一个参数化存储历程的前往值来添补数据集,必需在数据会见类的某个办法中有上面的代码:
Dim_pfAsNewProviderFactory(ProviderType.SqlClient)
DimcnAsIDbConnection=_pf.CreateConnection(_connect)
DimdaAsIDataAdapter=_pf.CreateDataAdapter("usp_GetBook",cn)
DimdbAsIDbDataAdapter=CType(da,IDbDataAdapter)
db.SelectCommand.CommandType=CommandType.StoredProcedure
db.SelectCommand.Parameters.Add(_pf.CreateParameter("@productId",DbType.Int32,id))
DimdsAsNewDataSet("Books")
da.Fill(ds)
典范的情形是你在类的条理声明ProviderFactory变量并在数据会见类的机关函数中实例化它。别的,它的机关函数与从设置文件中读取的供应程序一同组装,而不该该是硬代码。你能够设想,ProviderFactory是数据会见类的一个严重的增补,而且能被包含进部件,分发给别的的开辟职员。
结论
在Web服务时期将创建愈来愈多的使用程序操纵来自自力的使用程序层的数据。假如你遵守一些基础划定规矩并构成习气,编写数据会见代码将更快、更简单,而且更能从头利用,把你的毛病保留到服务器,同意你坚持数据自力。
不过你如果学.net的话,你就不要选os了,这课比较底层的。你可以旁听数据库加上软件构件和中间件。(webservices和面向服务的课也应该听一听) CGI程序在运行的时候,首先是客户向服务器上的CGI程序发送一个请求,服务器接收到客户的请求后,就会打开一个新的Process(进程)来执行CGI程序,处理客户的请求。CGI程序最后将执行的结果(HTML页面代码)传回给客户。 碰到复杂点的问题都不知道能不能解决,现在有点实力的公司都选择自已在开源的基础上做开发。但没听说过有人在IIS上做改进的,windows、sqlserver集群方面的应用也很少见。 主流网站开发语言之PHP:PHP的全名非常有趣,它是一个巢状的缩写名称——“PHP:HypertextPreprocessor”,打开缩写还是缩写。PHP是一种HTML内嵌式的语言(就像上面讲的ASP那样)。而PHP独特的语法混合了C,Java,Perl以及PHP式的新语法。它可以比CGI或者Perl更快速地执行动态网页。 但是java靠开源打出的一片天地,特别是在微软的垄断下能打开今天的局面还是有它的生命力的。 CGI程序在运行的时候,首先是客户向服务器上的CGI程序发送一个请求,服务器接收到客户的请求后,就会打开一个新的Process(进程)来执行CGI程序,处理客户的请求。CGI程序最后将执行的结果(HTML页面代码)传回给客户。 现在的ASP.net分为两个版本:1.1和2.0Asp.net1.1用VS2003(visualstudio2003)编程。Asp.net2.0用VS2005(visualstudio2005)编程。现在一般开发用的是VS2003。 微软又推出ASP.NET。这不是ASP的简单升级,而是全新一代的动态网页实现系统,用于一台WEB服务器建立强大的应用程序。是微软发展的新体系结构.NET的一部分,是ASP和.NET技术的结合。
页:
[1]