您的位置:首页 > 编程语言 > ASP

利用同一 ASP.NET 的多个代码框架

2013-06-20 14:00 274 查看
2012年,Microsoft推出了两个添加到ASP.NET工具包的新框架:WebAPI和SignalR。这两个框架为开发环境带来独特的开发方式,每个框架都有自身的独特之处:

WebAPI为开发人员提供了类似MVC的体验,以交付针对机器解释的内容。没有用户界面,并且事务以RESTful的方式出现。内容类型经过协商后,基于提交到WebAPI端点的HTTP标头,WebAPI就可以将内容自动格式化为JSON或XML。

SignalR是来自Microsoft的新型“实时Web”交付模型。此技术打开了客户端-服务器通信通道,支持进行从服务器到客户端的即时丰富通信。由于是通过服务器调用客户端来实现内容交互,SignalR中的内容交付模型颠覆了我们的正常预期。

Web窗体和MVC之间以及WebAPI和MVC之间的利弊权衡如2所示。

2每个ASP.NET组件框架的优点

框架

效率

Control

UI

实时

Web表单





MVC





WebAPI





SignalR



工作效率与允许您快速开发和交付解决方案的功能相关。控制是可影响通过网络向连接用户传输的比特的程度。UI指示是否可以使用该框架来交付完整的UI。最后,“实时”表明框架能够在多大程度上及时显示即时更新的内容。

现在,在2013年,当我打开VisualStudio并试图启动一个ASP.NET项目时,我看到的是如34所示的对话框。

3VisualStudio2012中的新建Web项目

4VisualStudio2012中的新建项目模板对话框

在这些窗口中有一些棘手的问题。我应从什么类型的项目开始呢?我应使用什么模板才能最快获得解决方案呢?如果我想要添加每个模板的一些组件,将会怎样?我可以构建一个带有一些服务器控件和一个WebAPI的移动应用程序吗?

我只能选择一种方法吗?

我只能选择一种方法吗?简短的答案是否定的,您并非只能选择其中一种框架来构建Web应用程序。现在已有一些技术允许您将Web窗体和MVC结合在一起使用。与显示的对话框窗口不同,WebAPI和SignalR可以作为功能轻松添加到Web应用程序中。请记住,所有ASP.NET内容都是通过一系列HttpHandlers和HttpModules呈现的。只要引用了正确的处理程序和模块,就可以使用任何一种框架构来建解决方案。

这是“同一ASP.NET”概念的核心:不要只选择这些框架中的一个,应使用最符合您的需求的部分构建解决方案。您有很多选择,不要局限于其中的一种。

我们来具体看看这是怎么实现的。为此,我将创建一个小型的Web应用程序,其中包含统一布局、一个搜索屏幕和一个产品列表的创建屏幕。搜索屏幕将由Web窗体和WebAPI支持,并显示来自SignalR的实时更新。创建屏幕将由MVC模板自动生成。通过使用第三方控件库和面向ASP.NETAJAX的TelerikRadControls,我还将确保Web窗体具有精美的外观。这些控件的试用版可从bit.ly/15o2Oab获得。

设置“示例项目”和“共享布局”

我只需要使用3中所示的对话框创建一个项目就可以开始了。虽然我可以选择一个空的或Web窗体应用程序,可以选择的最完备解决方案则是MVC应用程序。以MVC项目开始是很好的选择,因为您从VisualStudio获得了所有的工具,可帮助您完成配置模型、视图和控制器的过程,并能够将Web窗体对象添加到项目文件结构中的任何位置。通过更改.csproj文件中的一些XML内容,可将MVC工具添加回现有Web应用程序。此过程可通过安装名为AddMvc3ToWebForms的NuGet包自动完成。

若要配置在这个项目中使用的Telerik控件,我需要在Web.config中进行一些更改,以添加通常会在标准TelerikRadControls项目中配置的HttpHandlers和HttpModules。首先,我将添加几行来定义TelerikAJAX控件UI皮肤:


<addkey="Telerik.Skin"value="WebBlue"/>[/code]
</appSettings>[code]


接下来,添加Telerik标签前缀:


<addtagPrefix="telerik"namespace="Telerik.Web.UI"assembly="Telerik.Web.UI"/>[/code]
</controls>[code]


我将为Telerik控件的Web.configHttp­Handlers添加最少的内容:


<addpath="Telerik.Web.UI.WebResource.axd"type="Telerik.Web.UI.WebResource"[/code]
verb="*"validate="false"/>

</httpHandlers>[code]


最后,我将添加到Telerik控件的Web.configHandlers:


<system.WebServer>[/code]
<validationvalidateIntegratedModeConfiguration="false"/>

<handlers>

<removename="Telerik_Web_UI_WebResource_axd"/>

<addname="Telerik_Web_UI_WebResource_axd"

path="Telerik.Web.UI.WebResource.axd"

type="Telerik.Web.UI.WebResource"verb="*"preCondition="integratedMode"/>[code]


现在,我要为这个项目创建一个布局页,所以我将在“视图”|“共享”文件夹中创建一个Web窗体site.master页。对于此站点布局,我要将标准的徽标和菜单添加到所有页。我将通过简单地将图像拖到布局上来添加一个徽标图像。接下来,为了将一个主要的级联菜单添加到布局,我将从控件工具箱把RadMenu拖到图像正下方的设计器上。从设计器图面,通过右键单击菜单控件并选择“编辑项目”以得到5所示的窗口,我可以快速构建菜单。

5TelerikRadMenu配置窗口

我要重点关注的两个菜单项位于“产品”下:“搜索”和“新建”。对于每个项目,我已对NavigateUrl属性和文本进行如下设置:


<telerik:RadMenuItemText="Products">[/code]
<Items>

<telerik:RadMenuItemText="Search"NavigateUrl="~/Product/Search"/>

<telerik:RadMenuItemText="New"NavigateUrl="~/Product/New"/>

</Items>

</telerik:RadMenuItem>[code]


菜单配置好以后,我现在遇到了新问题:我使用Web窗体定义布局,但需要承载MVC内容。这不是一个简单的问题,但它可以解决。

弥合鸿沟—将MVC配置为使用Web窗体母版页

像大多数人一样,我喜欢让事情变得简单。我来分享一下我为这个介于Web窗体和MVC之间的项目定义的布局。MattHawley设计了一项技术(有完善的文档),演示了如何结合使用Web窗体母版页和基于MVCRazor的视图(bit.ly/ehVY3H)。我将在这个项目中使用该技术。为了创建这样一个桥梁,我将配置一个引用母版页的简单Web窗体视图,称为RazorView.aspx:

1.

2.<%@PageLanguage="C#"AutoEventWireup="true"

3.MasterPageFile="~/Views/Shared/Site.Master"

4.Inherits="System.Web.Mvc.ViewPage<dynamic>"%>

5.<%@ImportNamespace="System.Web.Mvc"%>

6.<asp:Contentid="bodyContent"runat="server"

7.ContentPlaceHolderID="body">

8.<%Html.RenderPartial((string)ViewBag._ViewName);%>

9.</asp:Content>

10.

为了让我的MVC控制器使用此视图,并使其基于Razor的视图得到执行,我需要对每个控制器进行扩展,以正确路由视图内容。这通过一个扩展方法来实现,该方法通过RazorView.aspx对模型、ViewData和TempData重新进行路由,如6所示。

6通过Web窗体母版页重新路由MVC视图的RazorView扩展方法

1.

2.publicstaticViewResultRazorView(thisControllercontroller,

3.stringviewName=null,objectmodel=null)

4.{

5.if(model!=null)

6.controller.ViewData.Model=model;

7.controller.ViewBag._ViewName=!string.IsNullOrEmpty(viewName)

8.?

9.viewName

10.:controller.RouteData.GetRequiredString("action");

11.returnnewViewResult

12.{

13.ViewName="RazorView",

14.ViewData=controller.ViewData,

15.TempData=controller.TempData

16.};

17.}

18.

构建此方法后,我就可以通过母版页轻松路由所有MVC操作。下一个步骤是设置ProductsController以便能够创建产品。

MVC和创建产品屏幕

此解决方案的MVC部分遵循相当标准的MVC方法。在我的项目的“模型”文件夹,我定义了一个简单的模型对象,称为BoardGame,如7所示。

7BoardGame对象

1.

2.publicclassBoardGame

3.{

4.publicintId{get;set;}

5.publicstringName{get;set;}

6.[DisplayFormat(DataFormatString="$0.00")]

7.publicdecimalPrice{get;set;}

8.[Display(Name="Numberofitemsinstock"),Range(0,10000)]

9.publicintNumInStock{get;set;}

10.}

11.

接下来,我使用VisualStudio中标准的MVC工具创建一个空的ProductController。我将添加“视图”|“产品”文件夹,然后右键单击“产品”文件夹,再从“添加”菜单选择“视图”。此视图将支持新BoardGame的创建,所以我将使用8中所示的选项创建。

8创建新建视图

由于使用了MVC工具和模板,我不需要进行任何更改。创建的视图带有标签和验证,并可以使用我的母版页。9显示如何在ProductController中定义“新建”操作。

9通过RazorViewProductController路由

1.

2.publicActionResultNew()

3.{

4.returnthis.RazorView();

5.}

6.[HttpPost]

7.publicActionResultNew(BoardGamenewGame)

8.{

9.if(!ModelState.IsValid)

10.{

11.returnthis.RazorView();

12.}

13.newGame.Id=_Products.Count+1;

14._Products.Add(newGame);

15.returnRedirect("~/Product/Search");

16.}

17.

MVC开发人员应熟悉此语法,唯一的变化是返回一个RazorView,而不是视图。_Products对象是一个此控制器中所定义的虚产品的静态只读集合,而不是使用此示例中的数据库:

1.

2.publicstaticreadonlyList<BoardGame>_Products=

3.newList<BoardGame>()

4.{

5.newBoardGame(){Id=1,Name="Chess",Price=9.99M},

6.newBoardGame(){Id=2,Name="Checkers",Price=7.99M},

7.newBoardGame(){Id=3,Name="Battleship",Price=8.99M},

8.newBoardGame(){Id=4,Name="Backgammon",Price=12.99M}

9.};

10.

配置基于Web窗体的搜索页

我希望用户访问产品搜索页面的URL有别于Web窗体的URL,能够便于用户搜索。随着ASP.NET2012.2的发布,现在可以轻松完成这一配置。只需打开App_Start/RouteConfig.cs文件,并调用EnableFriendlyUrls以启动此功能:

1.

2.publicstaticvoidRegisterRoutes(

3.RouteCollectionroutes)

4.{

5.routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

6.routes.EnableFriendlyUrls();

7.routes.MapRoute(

8.name:"Default",

9.url:"{controller}/{action}/{id}",

10.defaults:new{controller="Home",action=

11."Index",id=UrlParameter.Optional}

12.);

13.}

14.

添加这一行后,ASP.NET将把/Product/Search请求路由到位于/Product/Search.aspx的物理文件

接下来,我要配置一个显示目前产品及其库存水平的网格的搜索页面。我将在我的项目中创建一个“产品”文件夹并向其添加一个名为Search.aspx的新Web窗体。在此文件中,我将去掉除@Page指令之外的所有标记,并将MasterPageFile设置为前面定义的Site.Master文件。为了显示我的结果,我将选择TelerikRadGrid,这样我就可以快速配置并显示结果数据:

1.

2.<%@PageLanguage="C#"AutoEventWireup="true"

3.CodeBehind="Search.aspx.cs"

4.Inherits="MvcApplication1.Product.Search"

5.MasterPageFile="~/Views/Shared/Site.Master"%>

6.<asp:Contentrunat="server"id="main"ContentPlaceHolderID="body">

7.<telerik:RadGridID="searchProducts"runat="server"width="500"

8.AllowFilteringByColumn="True"CellSpacing="0"GridLines="None"

9.AllowSorting="True">

10.

网格将自动生成绑定到其上的列,并提供排序和筛选功能。不过,我希望提高这一过程的动态性。我想在客户端实现数据的交付和管理。在此模型中,数据可以在Web窗体中无服务器端代码的情况下被发送并绑定。为此,我将添加一个负责交付并执行数据操作的WebAPI。

向组合中添加WebAPI

使用标准的“项目”|“新增”菜单,我将一个名为ProductController的WebAPI控制器添加到我的项目中名为“api”的文件夹。这有助于我清楚了解MVC控制器和API控制器之间的差别。此API将完成一项工作—以JSON格式交付网格数据并支持OData查询。要在WebAPI中完成这一点,我将编写一个Get方法并为其添加Queryable属性:

1.

2.[Queryable]

3.publicIQueryable<dynamic>Get(ODataQueryOptionsoptions)

4.{

5.returnControllers.ProductController._Products.Select(b=>new

6.{

7.Id=b.Id,

8.Name=b.Name,

9.NumInStock=b.NumInStock,

10.Price=b.Price.ToString("$0.00")

11.}).AsQueryable();

12.}

13.

此代码经过少许格式化处理之后,返回静态列表中的BoardGame对象集合。通过使用[Queryable]修饰该方法并返回可查询的集合,WebAPI框架会自动接手处理OData筛选和排序命令。该方法还需要使用输入参数ODataQueryOptions进行配置,以便处理网格提交的筛选数据。

如果要在Search.aspx上配置网格以使用此新API,我需要向页面标记添加一些客户端设置。在此网格控件中,我使用ClientSettings元素和DataBinding设置定义客户端数据绑定。DataBinding设置列出了API的位置、响应格式类型和要查询的控制器名称,以及OData查询格式。通过这些设置和要在网格中显示的列的定义,我可以运行该项目,并看到绑定到_Products虚数据列表中数据的网格,如10所示。

10网格的完整格式源

1.

2.<telerik:RadGridID="searchProducts"runat="server"width="500"

3.AllowFilteringByColumn="True"CellSpacing="0"GridLines="None"

4.AllowSorting="True"AutoGenerateColumns="false"

5.>

6.<ClientSettingsAllowColumnsReorder="True"

7.ReorderColumnsOnClient="True"

8.ClientEvents-OnGridCreated="GridCreated">

9.<ScrollingAllowScroll="True"UseStaticHeaders="True"></Scrolling>

10.<DataBindingLocation="/api"ResponseType="JSON">

11.<DataServiceTableName="Product"Type="OData"/>

12.</DataBinding>

13.</ClientSettings>

14.<MasterTableViewClientDataKeyNames="Id"DataKeyNames="Id">

15.<Columns>

16.<telerik:GridBoundColumnDataField="Id"HeaderStyle-Width="0"

17.ItemStyle-Width="0"></telerik:GridBoundColumn>

18.<telerik:GridBoundColumnDataField="Name"HeaderText="Name"

19.HeaderStyle-Width="150"ItemStyle-Width="150">

20.</telerik:GridBoundColumn>

21.<telerik:GridBoundColumnItemStyle-CssClass="gridPrice"

22.DataField="Price"

23.HeaderText="Price"ItemStyle-HorizontalAlign="Right">

24.</telerik:GridBoundColumn>

25.<telerik:GridBoundColumnDataField="NumInStock"

26.ItemStyle-CssClass="numInStock"

27.HeaderText="#inStock"></telerik:GridBoundColumn>

28.</Columns>

29.</MasterTableView>

30.</telerik:RadGrid>

31.

使用实时数据激活网格

最后一个步骤是随着产品出货和进货实时显示库存水平变化的功能。我将添加一个SignalRhub以传输更新信息并在搜索网格上显示新值。要将SignalR添加到我的项目,我需要发出以下两个NuGet命令:


Install-Package-preMicrosoft.AspNet.SignalR.SystemWeb[/code]
Install-Package-preMicrosoft.AspNet.SignalR.JS[code]


这些命令将在IISWeb服务器中安装要承载的ASP.NET服务器组件,并为Web窗体启动客户端JavaScript库。

SignalR服务器端组件被称为Hub,我将定义我自己的Hub,方法是在我的Web项目中的Hubs文件夹添加一个名为StockHub的类。StockHub需从Microsoft.AspNet.SignalR.Hub类继承而得。我定义了一个静态的System.Timers.Timer,使应用程序能够模拟库存水平的变化。模拟方式是每隔2秒(触发定时器Elapsed事件处理程序),我会随机设置一个随机选择产品的库存水平。一旦设置了产品库存水平,通过在客户端执行一个名为setNewStockLevel的方法,我将通知所有连接的客户端,如11中所示。

11SignalRHub服务器端组件

1.

2.publicclassStockHub:Hub

3.{

4.publicstaticreadonlyTimer_Timer=newTimer();

5.privatestaticreadonlyRandom_Rdm=newRandom();

6.staticStockHub()

7.{

8._Timer.Interval=2000;

9._Timer.Elapsed+=_Timer_Elapsed;

10._Timer.Start();

11.}

12.staticvoid_Timer_Elapsed(objectsender,ElapsedEventArgse)

13.{

14.varproducts=ProductController._Products;

15.varp=products.Skip(_Rdm.Next(0,products.Count())).First();

16.varnewStockLevel=p.NumInStock+

17._Rdm.Next(-1*p.NumInStock,100);

18.p.NumInStock=newStockLevel;

19.varhub=GlobalHost.ConnectionManager.GetHubContext<StockHub>();

20.hub.Clients.All.setNewStockLevel(p.Id,newStockLevel);

21.}

22.}

23.

为使此Hub的数据可从服务器访问,我需要向RouteConfig添加一行,表明Hub的存在。通过在RouteConfig的RegisterRoutes方法中调用routes.MapHubs,我完成了SignalR服务器端的配置。

接下来,网格需要侦听这些来自服务器的事件。为此,我需要添加一些从NuGet安装的SignalR客户端库和MapHubs命令生成的代码的JavaScript引用。SignalR服务使用12中显示的代码连接并公开客户端上的setNewStockLevel方法。

12用于激活网格的SignalR客户端代码

1.

2.<scriptsrc="/Scripts/jquery.signalR-1.0.0-rc2.min.js"></script>

3.<scriptsrc="/signalr/hubs"></script>

4.<scripttype="text/javascript">

5.vargrid;

6.$().ready(function(){

7.varstockWatcher=$.connection.stockHub;

8.stockWatcher.client.setNewStockLevel=function(id,newValue){

9.varrow=GetRow(id);

10.varorgColor=row.css("background-color");

11.row.find(".

12.numInStock").animate({

13.backgroundColor:"#FFEFD5"

14.},1000,"swing",function(){

15.row.find(".

16.numInStock").html(newValue).animate({

17.backgroundColor:orgColor

18.},1000)

19.});

20.};

21.$.connection.hub.start();

22.})

23.</script>

24.

在jQuery就绪事件处理程序中,我使用$.connection.stockHub语法建立了对StockHub的引用,名为stockWatcher。然后为stockWatcher的客户端属性定义了setNewStockLevel方法。此方法使用其他一些JavaScript帮助器方法遍历网格,找到相应产品的行,并使用jQueryUI提供的绚丽动画更改库存水平,如13所示。

13WebAPI生成并由SignalR维护的网格搜索界面

总结

至此,我演示了如何构建一个ASP.NETMVC项目并添加Web窗体布局、第三方AJAX控件及相应的Web窗体。我使用MVC工具生成用户界面,并使用WebAPI和SignalR激活内容。此项目利用各组件的最佳功能,使用来自所有四个ASP.NET框架的功能,提供了一个一致的界面。您也来试试吧。对于您以后的项目,您将不必局限于一种ASP.NET框架。而是要各取所需。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐