UML软件工程组织

整合.NET技术
By Alan Gordon 选择自 发赛特技术网

使用VS.NET引进的技术,你能够比以前更容易地建立一个电子商务网站。使用VS.NET和.NET Framework SDK你可以用很少的代码去建立一个完善的Web应用程序,这些代码比起大多数ASP应用程序中使用的一大堆混乱的脚本和HTML代码,它更灵活,更易维护。


			图1.
图1. 构建架构
我来向大家展示如何建立一个在线书店的应用程序,我们把这个应用程序叫.NET Books。这个程序有点模仿Visual Studio .NET中带的Duwamish例子。但它也和服务组件结合。服务组件是使用COM+的.NET组件-增加微软消息队列(MSMQ)和BizTalk Orchestration. (注意服务组件也许只存在于beta 版中。) 三层应用(看图1)结合了几个另外的.NET技术,包括Web forms, Web user controls, Web Service, 分布式处理, 连接池(connection pooling), 和结构字符串。读完这篇文章后,你将会知道如何结合多种.NET技术自己设计、建立和发布你自己的应用程序。

书店展示了现有的一些图书和不久将出版的一些图书,这些图书是关于.NET或者与.NET相关的,比如XML、COM+、SQL Server和BizTalk Server。事实上,我发现在“今日专刊”上摆放的图书对开发这个程序也是很有用的(见图2)。.NET Books应用程序也可以在SQL7.0上运行。它没有使用任何SQL Server 2000的特性。

 

				图2.
图2. 用户发现主页
当用户向.NET Books下了一个订单后,它就会把订单的细节写入数据库,并向一个私有MSMQ队列写一个描述订单的XML文档。我用Biz Talk Orchestration执行了一个商业过程,BizTalk Orchestration从队列中得到订单,然后根据定单中的信用卡信息做支付认可。

BizTalk Orchestration是微软BizTalk Server 2000产品的子集。它是一套用来建模、实施和完成复杂的商业过程的工具。BizTalk Orchestration是由 基于Visio 2000的设计工具,基于XML描述商业过程的被称之为XLANG的语言和执行XLANG商业过程的引擎组成。

 

			图3.
图3. 浏览图书信息
在内部,.NET Books商业过程使用一个随机数生成器来批准一些订单和拒绝一些订单。这个商业过程更新订单的状态以显示订单被成功支付或者被客户的信用卡拒绝了。

当用户访问.NET Books时,他或她可以根据一些识别信息比如标题或者作者去搜索一本书(见图3)。搜索结果返回符合条件图书的目录,当用户点击到标题上时,我们将连到关于这本书的更详细资料的页面上去。如果用户决定购买这本书,点击“加入购物车”把这本书加入到购物车,然后我们就到了购物车页面(见图4)。在这儿,用户可以看到当前所选的图书,可以编辑图书的数量,可以把数量设置为0以删除图书。当用户已经做出决定,他或她选择提交连接,当用户还没有登陆时,这个连接提示用户登陆。当用户已经登陆进去,他或她会被带到一个三步定购向导,在这里输入邮购地址和信用卡信息。第三步说明用户已经完成了订单,当用户点击这个页面上的确认按钮,订单被提交并开始处理。

当顾客提交了订单开始处理时,应用程序更新了这个订单的数据库信息,同时,向消息列表发送一个描述此订单的的XML文档。在分布式处理的过程中,这两个动作都被执行。如果任何一项操作失败,处理将会被退回,换句话说,如果XML文档没有被送到信息队列中去的话,数据库将不会更新,反之亦然。

建立搜索

			图4.
图4. 把书加到购物车
尽管.NET Books程序本身并没有什么独特,但是其新意在于使用.NET Framework和VS.NET使得开发这种应用程序变的更加容易。这个应用程序中的所有网页都包括在一个叫DotNetBooks的C# 网络应用程序项目中。这个项目中包括10 个Web Forms 页面、两个Web 用户控件、和一个用来执行购物车程序的C#类。默认页-default.aspx—中包含所有的搜索性能。如果你从任何一页选择去搜索,不管是用户定义的搜索,还是在一个选择预定义的目录中搜索,你都会被重新定向到默认页。传到这个页面的搜索字符串确定了搜索的类型(如通过标题)来搜索数据。例如,下面的地址用来搜索标题中包含C#的所有图书。(见列表1)
http://localhost/DotNetBooks/Default.aspx?SearchType
=Title&SearchData;=C#

搜索控件会出现在网站的大多数页面上,它被当作一个Web user controls来使用。Web user control是ASP.NET Web From代码的小片段。它被包装起来可以很容易的被其他页面重新使用。在项目菜单中选择增加Web User Control可以在项目中增加Web User Control。这样在VS.NET中将有一个表单,你可以象编辑其它Web Form一样来编辑这个表单。然后,VS.NET把这个表单存成扩展名为ASCX的文件。把Web User Control拖放到客户端的Web Form中使用它,这样,VS.NET就会加代码到客户端Web Form的顶部,并在客户端Web Form中注册Web User Control。
<%@ Register TagPrefix="DotNetBooks" TagName="Search" Src="SearchControl.ascx" %>

VS.NET也在客户端Web Form中下面的地方增加标识Web user control的标签如下:<DotNetBooks:Search id=SearchControl1 runat="server"></DotNetBooks:Search>

DataList、DataGrid和Reapeater控件是Web Forms工具库中功能最强大的控件。在你列出内容的每个地方都可以用DataList来显示,包括在搜索控件中的目录列表,和执行搜索后得到的图书列表。购物车页面使用DataGrid控件。你可以在各种不同的地方使用DataList控件,但是能够最强有力地和灵活地使用它的地方还是数据绑定。使用数据绑定最强有力的和灵活的地方是在项目模板中。

项目模板允许你定义一小段HTML代码来显示数据源中的每行数据(见列表2)。比如,列表2中的代码表示了用来展示执行搜索得到图书列表的项目模板。这个模板定义了HTML表。表中包含一个图象和两行超链接文字。DataBinder.Eval方法用来把数据源得到的数据显示在模板上。例如,下面的代码(从列表2中摘录)展示了如何把每本书的图片绑定到项目模板的图片控件上。
<td align="center" width="80"rowspan="2">
<asp:Image runat="SERVER"
ImageURL='<%# "images/books/small/" +
DataBinder.Eval(Container.DataItem,
"ImageFile") %>'
ImageAlign=
"Middle" ID=Image2>
</asp:Image>
</td>

《ASP+预览》的第三章有关于DataList, DataGrid, 和 Repeater控件的详细讨论。

设计购物车
几乎所有的电子商务网站都有一个购物车,当客户在网站上浏览时,这就说明如果想购买的话他们可以把商品加入到购物车中。顾客也可以修改购物车中商品的数量或者删除他们。如果你用ASP建立应用程序,有两种方法可以实现购物车功能:一是把商品信息存储到Session对象中,二是把商品信息存储到数据库中去,仅在Session对象中有一个索引。把购物车存储在Session中很容易也会很快,当然我们假设你的代码没有问题。不幸的是,在ASP中Session对象被当作储存的数据结构执行,这些结构放在IIS的进程中。这就会引起两个主要问题:首先当IIS进程失败或者关闭时,所有的Session对象就会丢失;再者,在服务器上运行网站会变得更复杂,这是因为Session被绑定在产生Session的机器的IIS进程中。为了在网页中使用Session,你必须能够保证客户端总是返回到同一个服务器,否则客户端的Session就会丢失。

ASP.NET解决了这些问题。现在,你有三个选择来存储Session.首先,在默认情况下你可以放在当前主机上的网络服务的进程中,这点和ASP一样。第二,Session可以存储在网络服务的一个独立的进程中,这个网络服务是在你的服务器群的机器上。你的服务器群的其他机器也可以访问这个进程。最后,你可以存储在SQL Server中。你可以使用你的网络程序中的配置文件(Config.web)来选择你的Session信息存储在什么地方。你向Session中写信息或者从Session中读信息,将不会根据你选择哪儿存储Session对象而改变。下面的代码展示了.NET Books中的网页如何去Session对象中取回购物车。
public Cart GetShoppingCart()
 {
// Try to get the cart from Session
   Cart shoppingCart =(Cart)(Session["ShoppingCart"]);
    if (null == shoppingCart)
     {
	// If there is no cart, create it now
	shoppingCart = new Cart();
	// Save the cart for later use
	Session.Add("ShoppingCart",shoppingCart);
     }
     return shoppingCart;
  }

这个逻辑对于使用ASP的人是非常熟悉的。Rob Howard写了一篇关于ASP.NET session state的精彩的文章(见资源)。同样,你也可以看.NET Framework开发者向导中被称为“Session State”的文章。

对于这个应用程序,我建立了一个类叫Cart,它提供很方便的方法来更改购物车中商品的数量、把购物车发送到服务器(申请订单)、和在订单发送后清空购物车(见表1)。购物车的数据是按照严格的数据类型来储存的,这个类型是从我所建立的一个XML Schema (XSD)文件中得来的。

为了从XSD schema文件中生成一个严格的数据类型,首先,你必须把XSD文件增加到VS.NET项目中。当你双击这个文件,VS.NET在一个可视化的编辑器中显示这个XML schema文件(见图5)。右击XML schema编辑器选择Generate DataSet来建立一个严格的数据类型来匹配schema。你可以用处理从数据库的来的数据的方法来处理这个数据。当你的数据集中使用Xml或者XmlData属性时,它建立一个XML 文档,这个XML文档使用数据集产生的那个XML schema。

这个功能包含了另外一点,即和BizTalk 2000的整合性。你可以选择一个BizTalk schema或者在BizTalk编辑器中建立一个XML schema,在VS.NET中来读,然后根据这个schema建立一个严格的数据类型的数据集。我打算使用这个方法,但是因为BizTalk 2000 beta生成XDR schema, 所以没有办法实现。在.NET Framework中包含能够把XDR schema转化为XSD schema的XML schema定义工具(见.NET Framework SDK中更多的关于xsd.exe的信息)。

我试图使用这个方法,但却不断的陷入这个beta版的bugs中。最后,我放弃了这个想法,直接用VS.NET编辑器建立了一个简单的XML schema。当用户下了一个订单后,应用程序把数据集送到Web Service,Web Service从数据集提取出定单信息,然后调用存储过程向数据库中插入数据。Web Service使用数据集中的XML属性返回一个XML文档,并把这个文档送到微软信息列表中去。

安全机制
商业应用
.NET降低了开发成本
通过使用VS.NET和.NET Framework提供的工具你可以灵活且高效的开发B2B或B2C应用程序。 你所要知道的是哪些工具可用,他们能做什么,如何把他们结合起来使用来简化从概念到开发的过程。 理解.NET不同功能之间的关系可以加强你项目管理的能力。
安全,特别是鉴定和授权,在ASP.NET中比ASP中更容易实现。鉴定是用户向一个计算机系统证明他或者她的身份的过程。典型地说,就是用户提供一个包含用户名和密码组合的证明。授权是限制用户访问基于身份的资源的过程。大多数电子商务网站允许用户不通过鉴定就可以浏览并向购物车增加商品。仅仅当用户下订单的时候,才需要鉴定,这时要求用户输入用户名和密码。使用ASP你可以通过在Session中存入一个布尔型的标志来说明用户是否登陆来实现鉴定。如果用户浏览到一个需要授权的网页上,那就使用户连到登陆页面。如果用户输入有效的用户名和密码,你把用户连到他起先打算访问的网页上,并且在Session中设置布尔型标志以说明用户已鉴定过了。

ASP.NET提供了所有的这些鉴定功能。你必须首先选择一个可获得的鉴定方法。你可以选择:Windows, Passport, Forms(在beta 1中叫Cookie)和none。(关于选择的更多信息见资源中“ASP.NET鉴定”)。在.NET Books程序中使用Forms。在你的应用程序的config.web文件中选择ASP.NET鉴定方法。下面代码展示如何选择一个Forms鉴定。
<authentication mode="Forms">
  <forms name="DOTNETBOOKSCOOKIE"
loginUrl="login.aspx"
protection="Encryption">
  </forms>
</authentication>

在这个例子中,你确认了系统将会使用的cookie名(DOTNETBOOKSCOOKIE)和当用户需要授权时被重新指向的地址(login.aspx)。一旦你选择了鉴定方法,你可以在Config.web文件中用下面的方法限制访问到特定页面的用户:
<location path="CheckOut1.aspx">
  <system.web>
    <authorization>
      <deny users="?" />
    </authorization>
  </system.web>
</location> 

这儿你拒绝匿名访问CheckOut1.aspx(订单向导的第一页)。因此,没有登陆的用户将会被自动指到login.aspx。在login.aspx里,用户可以输入用户名和密码。然后login.aspx将会调用.NET Books Web Service中的登陆方法。如果用户名和密码在数据库中存在,登陆方法返回一个成功代码并连到CheckOut1.aspx,否则,将返回一个错误信息。

分离商业逻辑
.NET Books网站的商业逻辑作为一个Web Service来实现。你能够在DotNetBooksWebService (见表2,从.NET 杂志网站下载代码)C#网络服务项目发现这个Web Service所有的代码。把这个网站的商业逻辑分成网络服务,这就使得在你的网站的其他部分重新使用这些商业逻辑变得更容易了。例如,建立一个图书俱乐部的网站,其中使用我们的网络服务来搜索和定购图书。现在已经有好多文章介绍如何用.NET建立网络服务(见资源中我的Visual C++开发期刊),所以我就不再介绍如何建立网络服务的基本知识了了。然而,有关如何使用COM+服务的文章很少,比如分布式处理和对象池(object pooling)(和.NET中的消息队列),所以我将展示在你的网络服务中如何用这些技术。

下面的代码展示了如何把.NET类转化成一个服务组件,黑体表示重要的部分。
using System.EnterpriseServices;;[Transaction(TransactionOption.Required)]
public class Books :ServicedComponent
  {
    [WebMethod]
    [AutoComplete]
     public DataSet GetBooksByTitle(string title)
     {
	  //…
     }
   }

为了建立一个服务组件,你首先要参考System.EnterpriseServices集合,使用“using”声明来增加参考。接着,你要给服务组件增加一个特性来说明你想用哪个COM+服务。在System.EnterpriseServices名字空间的文档中,你能发现现有的COM+相关特性的完全列表。在这个例子中,我使用事务处理特性,注明这个类要求事务处理。服务组件也必须继承于一个称为ServicedComponent的类,这个类你也可以从System.EnterpriseServices名字空间中找到。Books类使用叫AutoComplete的COM+特性。这个特性当方法成功返回时调用SetComplete()自动完成商品交易,当方法抛出异常时调用SetAbort()。

当客户端实例化了一个服务组件类,如果COM+应用程序不存在的话,Common Language Runtime(CLR)建立了一个COM+应用程序来运行这个类,然后增加服务组件和它的特性一起到COM+应用程序。你把下面代码加入到你的集合文件(在VS.NET 项目的assemblyInfo.cs)中,这些代码就可以控制COM+应用程序如何被建立,也可以影响在COM+应用程序中使用的应用层COM+特性:
[assembly: ApplicationName("DotNetBooksApp")]
[assembly: ApplicationActivation(ActivationOption.Library)]
[assembly: Description("A COM+ Application to test .NET Serviced Components")]

这儿你详细了解了DotNetBooksApp COM+应用程序,并且使用了类库(过程中)激活。

如果你建立一个服务组件类同时它也是一个网络服务的话,你需要注意下面三点:首先,这个类不能象大多数网络服务那样从WebService中继承。你不能从WebService继承是因为C#只能从一个单独的类继承,而服务组件类必须从ServicedComponent基类继承。如果你没有从一个WebService基类继承,你就不能访问Session、Application、和Context对象,但是其它的网络服务功能和期待的一样都可以使用。第二点:如果你想使用服务组件来实现网络服务,你必须在使用服务组件之前,把包含服务组件的集合代码增加到公共集合缓冲Global Assembly Cache (GAC)中去。这个代码可以使IIS能够发现服务组件类。服务组件必须有一个引人注目的名字。你可以使用共享的命名工具(sn.exe)来起一个引人注目的名字,然后通过把下面代码加到你的集合信息文件中来把这个名字加到集合中去。
[assembly: AssemblyKeyFile("filenameforkey")]

第三点是VS.NET将不会在其设计器中显示从服务组件继承来的网络服务。你将会看到一个错误提示:“基类System.EnterpriseServices.ServicedComponent不能被设计”。这个错误并不能阻止你看到网络服务的代码。如果你必须使用设计器的话,那你就把它存为服务组件,直到你不再使用设计器为止。

如果你想使用分布处理,但却不想建立一个服务组件的话,你也可以象概括表单所显示的那样设置在WebMethod特性中的TransactionOption属性。
public class Books : WebService
{
  [WebMethod(TransactionOption=TransactionOption.Required)]
    [AutoComplete]
    public DataSet GetBooksByTitle
     (string title)
     {
	//…
     }
}

使用这个方法,你可以使用能够访问Session、Application和Context对象的网络服务基类。
记住,不管你通过服务组件还是通过网络服务特性的TransactionOption属性来做分布处理,你都要协调涉及多个资源管理器的处理,这些资源管理器包括SQL Server 和 MSMQ,在我们例子中是一个单一的网络服务。目前没有机构可以协调包括多个网络服务的处理。这个功能要求扩展到简单对象访问协议Simple Object Access Protocol (SOAP)上去。

你的网络服务使用ADO.NET去从数据库中读数据和向数据库中写数据。网络服务中大多数的方法是发送0个或者多个参数然后向客户端返回一个数据集的搜索。为了最大化的代码再利用,我建立了一个叫做GetDataSetFromStoredProc的方法。这个方法中包括了调用储存过程返回数据集(见列表3)的大多数ADO.NET逻辑。GetBooksByISBN方法建立了一个参数,给这个参数正确的类型和值,然后把这个参数和存储过程的名称一起传递到GetDataSetFromStoredProc方法中(见列表4)。

网络服务中最复杂的方法是InsertOrder()方法,如果你从.NET 杂志网站下载代码的话你可以看到这个方法。当用户确认下定单时,客户端调用这个方法。这个方法执行了两个主要功能:把订单加入到数据库,把XML文档送到信息队列。

为了最大可能的实现和可扩展性,我建立了一个存储过程,也叫InsertOrder,这个存储过程使得我们一次调用就可以向数据库中增加用户和详细信息。InsertOrder存储过程接受顾客ID、通讯地址、信用卡信息,和订单总数及包含订单信息的限定的字段:Item[1].ID,Item[1].Quantity,
Item[1].UnitPrice/ Item[2].ID,Item[2].Quantity,
Item[2].UnitPrice



InsertOrder存储过程解析字符串,并把订单细节增加到数据库的OrderItems表中。InsertOrder()方法把购物车信息当作输入信息。这个方法从数据集中提取出顾客ID、通讯地址,和信用卡信息。然后把订单细节信息转化成显示格式的字符串。然后把字符串和订单参数传给InsertOrder存储过程。

当InsertOrder()方法把定单信息增加到数据库之后,它把包含定单信息的XML文档写入消息队列中去。网络服务在发送消息之前检查私有消息队列“DevDotNet”是否存在。你必须在运行网络服务的主机上建立这个队列。

.NET框架包括许多消息转换器,你可以用它们把.NET对象写到消息队列中去。在这个应用程序中,我们使用ActiveXMessageFormatter,这是因为我们用这个转换器发送到队列中的消息是很容易在BizTalk Orchestration商业过程中使用的VB6 COM对象访问的。

使用BizTalk Orchestration
我决定使用BizTalk Orchestration来执行支付授权商业过程。这个商业过程使用一个随机数生成器来批准一些订单和拒绝其它订单。它然后更新订单的状况以显示其是否成功交易。你必须拥有BizTalk 2000 Server的最终版本来使用支付授权商业过程,但是如果你没有,.NET Books应用程序的其它部分将仍然继续工作。


图6. 商业过程建模 
BizTalk Orchestration是用来帮助建模、执行和完成复杂商业过程的一套工具(见图6)。
这种技术的关键在于XLANG,XLANG是用来描述商业过程的XML语言。使用BizTalk Orchestration是很简单的,商业分析家用BizTalk Orchestration设计器建立一个流程,这种设计器是带了一个特殊模板的Visio 2000。然后开发者建立COM对象、脚本组件、和消息队列来执行商业过程。开发者把这些对象、脚本组件、和消息队列连接到流程图里。然后定义在对象、脚本组件、和队列之间流动的数据 ,并编译Visio图成为一个XLANG文件。然后开发者能够使用BizTalk 服务中的XLANG安排引擎执行XlANG文件。

为了运行商业过程,确信你建立了DevDotNet消息队列,然后在网站上建立一对订单。.NET Books网络服务中的InsertOrder方法向消息队列发送订单。从Start\Programs\Administrative Tools\打开计算机管理浏览器,验证订单存在在DevDotNet消息队列中。记下队列中的订单编号。

现在我们进入这篇文章代码中BizTalk子目录下面的RunSchedule目录。运行ExecuteOrderProcess.exe文件。下载订购过程XLANG文件(orderprocess.skx),点击运行XLANG进程按钮。你就会看到一条消息显示商业过程运行成功。当你在回到DevDotNet消息队列中,第一条信息已经从队列中移走了。再看数据库中的定单表,你将会 看到从消息队列中移去的订单号的状态已经发生了改变,如果是1,则表明信用卡得到授权划款,如果是-1,则表明信用卡被拒绝了。这篇文章的代码中包括一篇Word文档,文中一步步描述了如何在BizTalk Orchestration中构建商业过程。

项目中还有许多工作要做。网页可以变得更漂亮些。如果网站能够被一个有经验的网站设计师重新设计界面的话,我将会更感兴趣。我也没有时间实现帐户管理能力。换句话说,用户应该能够看到没有提交的订单,能够改变地址和交易信息。数据库设计也可以支持顾客填写多个地址。你可以根据这些扩充来改变界面。你也可以试着在网络服务中不通过服务组件来执行分布式处理,取而代之,使用WebMethod属性的TransactionOption特性。


关于作者:
Alan Gordon是FieldCentrix的项目管理者。FieldCentrix主要开发应用在服务行业的基于Internet的软件。他在UCLA Extension教过有关COM的课程,也是“The COM and COM+ Programming Primer (Prentice Hall, 2000)“的作者。目前他正在为COM+的程序员写.NET的书籍,这本书将于2001年由Apress出版。你可以通过agordon@fieldcentrix.com与他联系。

 

版权所有:UML软件工程组织