本文章是由機器翻譯。

ASP.NET

以單一 ASP.NET 運用多重程式碼架構

Jeff Fritz

2001 年,當微軟介紹 Microsoft.NET 框架和與它一種新技術稱為ASP.NET,Web 開發人員擁抱它為建築地盤使用基於表單的框架。這一框架,稱為 Web 表單,站在時間的考驗,八年,增強和更改以支援不斷變化的網路環境。創建一個 Web 應用程式在這段時間是一個簡單的選擇,提出四個ASP.NET選項,如中所示的新專案對話方塊圖 1。我們大多數人忽略ASP.NET移動 Web 網站和 Web 控制項庫專案,並建僅ASP.NETWeb 應用程式專案。如果您需要的 Web 服務,您將與.asmx 檔到現有的 Web 網站添加基於 SOAP 的服務。


圖 1 原始新專案ASP.NET的選擇 Visual C#

在 2009 年初,ASP.NET景觀大大改變了與介紹的模型-視圖-控制器 (MVC)。沒有更多的檢視狀態、 頁面事件生命週期或回發事件處理的承諾,與開發商蜂擁而至的新框架。我是其中之一,感興趣的這個更可測試的 Web 技術的潛力。我們必須找到方法來證明到我們的經理和成本中心的預算為切換到 MVC 中,應用程式和許多開發人員通過 MVC 內容的步驟開展工作提出了作為一個現有的 Web 表單應用程式的同一應用程式中。東西很好 MVC 幾年來,和共事然後 Web 有點長大。ASP.NET需要再進化。

在 2012 年,Microsoft 提供了兩個新的框架,將添加到ASP.NET工具組:Web API 和 SignalR。這些框架的兩個環境,帶來特別的東西,每個是獨特的在它自己的方式:

  • Web API 提供了一個 MVC 類似經驗為開發人員提供用於機解釋的內容。有沒有使用者介面,和寧靜的方式進行交易。內容類型進行談判,和 Web API 可以自動設置的格式內容 JSON 或 XML,基於提交到 Web API 的終結點的 HTTP 標頭。
  • SignalR 是從微軟新的"即時 Web"交付模型。這一技術開闢了用戶端-伺服器通信通道,以便立即的、 豐富的通訊從伺服器到用戶端。SignalR 中的內容交付模型反轉我們正常的期望,如,伺服器將調用用戶端與內容進行交互。

考慮與 Web API 和 MVC 中,已經看到 Web 表單和 MVC 之間的權衡,如中所示圖 2

圖 2 的每個ASP.NET元件框架的好處

Framework 產能 Control UI 真正的時間
Web Form    
MVC    
API web    
SignalR      

生產力包括的功能,讓您開發和快速地交付解決方案。控制是可以到的影響在您已連接的使用者在網路上傳輸的位的程度。UI 指示是否可以使用框架提供一個完整的使用者介面。最後,真正的時間建議如何好框架可被感知的立即更新及時地呈現內容。

現在,在 2013 年,當我打開我的Visual Studio的副本,請嘗試啟動ASP.NET專案,我面對中所示的對話方塊圖 3圖 4


圖 3 新的 Web 專案中Visual Studio2012


圖 4 新專案範本對話方塊中Visual Studio2012

有一些棘手的問題在這些視窗中。應該從什麼類型的專案開始與什麼範本會讓我接近我的解決方案,最快?和如果我想要包括的每個範本的某些元件嗎?我可以構建移動應用程式與一些伺服器控制項和 Web API 嗎?

我要選擇只是一種辦法嗎?

我要選擇只是一種辦法嗎?簡短的回答是不,你沒必要選擇這些框架構建一個 Web 應用程式中只有一個。有可用的技術,允許您使用 Web 表單和 MVC 在一起,並與提交對話方塊視窗,Web API 和 SignalR 可以方便地添加功能作為 Web 應用程式。請記住,通過一系列的 HttpHandlers 和 HttpModules 呈現ASP.NET的所有內容。只要引用了正確的處理常式和模組,您可以生成一個與這些框架的任何解決方案。

這是"一個ASP.NET"概念的核心:Don不選擇這些框架之一 — — 構建您的解決方案中最適合您需要的每個部分。你有很多選擇 ; 不要限制自己只是其中一個。

所以你可以看到這在行動中,我要在一起放一個小的 Web 應用程式將有一個統一的佈局、 搜索螢幕和創建螢幕上的產品的清單。搜索螢幕將會搭載 Web 表單和 Web API,並從 SignalR 顯示即時更新。創建螢幕將由 MVC 範本自動生成。我也要讓看起來很棒,通過使用協力廠商控制項庫,Telerik RadControlsASP.NETAJAX 的 Web 表單。試用版的這些控制項是可在 bit.ly/15o2Oab

設置的示例專案和共用的佈局

若要開始,我需要創建一個專案,利用中所示的對話方塊圖 3。雖然我可以選擇空或 Web 表單應用­陽離子、 最全面的解決方案,以選擇是 MVC 應用程式。MVC 專案從開始是工具的一個偉大的選擇,因為你所有從Visual Studio以説明您配置您的模型、 視圖和控制器,以及專案的檔結構中的任意位置添加 Web 表單物件的能力。它是可能要添加模具回現有的 Web 應用的 MVC­陽離子通過更改某些.csproj 檔中的 XML 內容。可以通過安裝稱為 AddMvc3ToWebForms 的 NuGet 包自動化這一過程。

若要在此專案中配置用於 Telerik 控制項,需要做一些駭客在 Web.config 中添加 HttpHandlers 和 HttpModules 通常會在一個標準的 Telerik RadControls 專案中配置。首先,我將添加幾行來定義 Telerik AJAX 控制項 UI 皮膚:

<add key="Telerik.Skin" value="WebBlue" />
</appSettings>

下一步,我將添加 Telerik 的標記首碼:

<add tagPrefix="telerik" namespace="Telerik.Web.UI" assembly="Telerik.Web.UI" />
</controls>

我會讓最小的增補需要到 Web.config Http­Telerik 控制項的處理常式:

<add path="Telerik.Web.UI.WebResource.axd" type="Telerik.Web.UI.WebResource"
    verb="*" validate="false" />
</httpHandlers>

並且,最後,我會添加到 Web.config Telerik 控制項處理常式:

<system.WebServer>
  <validation validateIntegratedModeConfiguration="false" />
  <handlers>
    <remove name="Telerik_Web_UI_WebResource_axd" />
    <add name="Telerik_Web_UI_WebResource_axd"
      path="Telerik.Web.UI.WebResource.axd"
      type="Telerik.Web.UI.WebResource" verb="*" preCondition="integratedMode" />

現在我想要為創建一個佈局頁這一專案,所以我會在視圖中創建一個 Web 表單 site.master 頁 |共用的資料夾。為此網站佈局,我想向所有頁面添加標準徽標和功能表。我將通過簡單地將圖像拖動到我的佈局添加徽標圖像。下一步,以添加到佈局的一個極大的級聯功能表,我會拖 RadMenu 從我的控制項工具箱拖到設計器中,圖像的下方。從設計器圖面,我可以快速構建出我的功能表功能表控制項上按右鍵,選擇編輯的專案,要在所示的視窗圖 5


圖 5 Telerik RadMenu 配置視窗

我想要集中的兩個功能表項目是根據產品 — — 搜索和新。對於每個這些專案中,我已經設置的 NavigateUrl 屬性和文本如下:

<telerik:RadMenuItem Text="Products">
  <Items>
    <telerik:RadMenuItem Text="Search" NavigateUrl="~/Product/Search" />
    <telerik:RadMenuItem Text="New" NavigateUrl="~/Product/New" />
  </Items>
</telerik:RadMenuItem>

與配置功能表,我現在有一個問題我已在其中定義我使用 Web 表單的佈局和需要對主機 MVC 內容。 這不是一個微不足道的問題,但它是一個可以解決的問題。

彌合鴻溝 — — 配置 MVC 使用 Web 表單的主頁面

大多數一樣的你,我更喜歡讓事情簡單。 我想分享我定義的版式為此 Web 表單和 MVC 之間的專案。 有發明的Matt霍利,演示如何使用 Web 表單的母版頁與基於 MVC 剃刀的意見詳細記錄的技術 (bit.ly/ehVY3H)。 我要在此專案中使用這種技術。 若要創建此橋,我將把配置簡單的 Web 表單檢視稱為引用母版頁的 RazorView.aspx:

<%@ Page Language="C#" AutoEventWireup="true"
  MasterPageFile="~/Views/Shared/Site.Master"
  Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<%@ Import Namespace="System.Web.Mvc" %>
<asp:Content id="bodyContent" runat="server" 
  ContentPlaceHolderID="body">
<% Html.RenderPartial((string)ViewBag._ViewName); %>
</asp:Content>

為了使我的 MVC 控制器使用此視圖,並允許其基於 Razor 視圖要執行,我需要擴展每個控制器來適當地路由查看的內容。 通過這種重排 ViewData 和適當地通過 RazorView.aspx,TempData 的模型的擴充方法中所示圖 6

圖 6 RazorView 擴充方法要重排 MVC 視圖通過 Web 表單主頁面

public static ViewResult RazorView(this Controller controller,
  string viewName = null, object model = null)
{
  if (model != null)
    controller.ViewData.Model = model;
  controller.ViewBag._ViewName = !string.IsNullOrEmpty(viewName)
    ?
viewName
    : controller.RouteData.GetRequiredString("action");
  return new ViewResult
  {
    ViewName = "RazorView",
    ViewData = controller.ViewData,
    TempData = controller.TempData
  };
}

使用此構造方法,我可以輕鬆地路由的所有母版頁通過 MVC 操作。 下一步是設置 ProductsController,這樣就可以創建產品。

MVC 和創建產品螢幕

此解決方案的 MVC 部分是相當標準的 MVC 方法。 我定義了一個簡單的模型物件,稱為桌游模型資料夾中的我的專案,如中所示圖 7

圖 7 桌游物件

public class BoardGame
{
  public int Id { get; set; }
  public string Name { get; set; }
  [DisplayFormat(DataFormatString="$0.00")]
  public decimal Price { get; set; }
  [Display(Name="Number of items in stock"), Range(0,10000)]
  public int NumInStock { get; set; }
}

我下一步,使用Visual Studio中模具的標準 MVC,創建空的 ProductController。我會將視圖添加 |產品資料夾,然後按右鍵產品資料夾並從添加功能表中選擇視圖。此視圖將支援創建的新的棋盤遊戲,因此,我會與中顯示的選項創建它圖 8


圖 8 創建"新的"視圖

MVC 模具和範本,我不需要改變任何東西。創建的視圖標籤和驗證,並可以使用我的母版頁。圖 9 顯示了如何在 ProductController 中定義新的行動。

圖 9 ProductController RazorView 通過路由

public ActionResult New()
{
  return this.RazorView();
}
[HttpPost]
public ActionResult New(BoardGame newGame)
{
  if (!ModelState.IsValid)
  {
    return this.RazorView();
  }
  newGame.Id = _Products.Count + 1;
  _Products.Add(newGame);
  return Redirect("~/Product/Search");
}

此語法應熟悉的 MVC 開發者,是唯一的改變是要返回 RazorView 而不是視圖。 _ 產品中心物件是虛擬產品在此控制器 (而不是在此示例中使用的資料庫) 中定義的靜態的唯讀集合:

public static readonly List<BoardGame> _Products = 
  new List<BoardGame>()
{
  new BoardGame() {Id=1, Name="Chess", Price=9.99M},
  new BoardGame() {Id=2, Name="Checkers", Price=7.99M},
  new BoardGame() {Id=3, Name="Battleship", Price=8.99M},
  new BoardGame() {Id=4, Name="Backgammon", Price= 12.99M}
};

配置 Web 表單基於搜尋網頁

我希望使用者能夠訪問同一個 URL,看起來不像它是一個 Web 表單的 URL,並且是友好要搜索的產品搜尋網頁面。 與ASP.NET2012.2 的發行,這現在可以配置很容易。 只需打開 App_Start/RouteConfig.cs 檔,並調用 EnableFriendlyUrls 來開啟這一功能:

public static void RegisterRoutes(
    RouteCollection routes)
  {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.EnableFriendlyUrls();
    routes.MapRoute(
      name: "Default",
      url: "{controller}/{action}/{id}",
      defaults: new { controller = "Home", action =
        "Index", id = UrlParameter.Optional }
    );
  }

與添加這行,ASP.NET將請求路由為 /Product/Search 到駐留在 /Product/Search.aspx 的物理檔

接下來,我想要配置顯示目前的產品和其庫存的水準網格的搜尋網頁面。 我會在我的專案中創建一個產品資料夾並添加新的 Web 表單給它命名為 Search.aspx。 在此檔中,我會刪除所有標記的 @Page 指令除外,將 MasterPageFile 設置為先前定義的 Site.Master 檔。 若要顯示我的結果,我會選擇 Telerik RadGrid,所以我可以快速配置和顯示結果資料:

<%@ Page Language="C#" AutoEventWireup="true"
  CodeBehind="Search.aspx.cs"
  Inherits="MvcApplication1.Product.Search"
  MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" id="main" ContentPlaceHolderID="body">
  <telerik:RadGrid ID="searchProducts" runat="server" width="500"
    AllowFilteringByColumn="True" CellSpacing="0" GridLines="None"
    AllowSorting="True">

網格將自動呈現列綁定到該伺服器上側和提供排序和篩選功能。 不過,我想這更有活力。 我想要看到的提供和管理用戶端上的資料。 在此模型中,資料可以傳輸和綁定的 Web 表單中的任何伺服器端代碼。 要做到這一點,我要添加一個 Web API,將提供和執行資料操作。

將 Web API 添加到組合

我將使用標準專案 |添加新的功能表以添加到名為"api"我的專案中的資料夾名為 ProductController 的 Web API 控制器。 這可以説明我保持清晰的 MVC 控制器和 API 控制器之間的差異。 此 API 要做一件事 — — 提供我網格的資料以 JSON 格式,並支援 OData 查詢。 若要實現此目的 Web API 中,我要寫一個 Get 方法和裝飾的可查詢的屬性:

[Queryable]
public IQueryable<dynamic> Get(ODataQueryOptions options)
{
  return Controllers.ProductController._Products.Select(b => new
  {
    Id = b.Id,
    Name = b.Name,
    NumInStock = b.NumInStock,
    Price = b.Price.ToString("$0.00")
  }).AsQueryable();
}

此代碼在靜態清單中用一小部分的格式返回我的桌游物件的集合。 通過裝飾具有 [Queryable] 的方法,並返回一個集合,可查詢,Web API 框架會自動處理和處理 OData 篩選和排序命令。 該方法還需要與 ODataQueryOptions 的輸入參數進行配置以處理提交的網格的篩選資料。

要配置網格上 Search.aspx 要消耗這個新的 API,需要將某些用戶端設置添加到頁的標記。 在這個網格控制項中,我的定義與 ClientSettings 元素和一個資料繫結設置的用戶端資料繫結。 資料繫結設置列出 API、 回應格式類型和查詢,以及 OData 查詢格式的控制器的名稱的位置。 與這些設置和一個列的定義,目前在網格中,我可以運行該專案,請參見綁定到資料清單中的 _ 產品中心虛擬的資料,作為網格圖 10 顯示。

圖 10 完成格式的網格的來源

<telerik:RadGrid ID="searchProducts" runat="server" width="500"
  AllowFilteringByColumn="True" CellSpacing="0" GridLines="None"
  AllowSorting="True" AutoGenerateColumns="false"
  >
    <ClientSettings AllowColumnsReorder="True"
      ReorderColumnsOnClient="True"
      ClientEvents-OnGridCreated="GridCreated">
      <Scrolling AllowScroll="True" UseStaticHeaders="True"></Scrolling>
      <DataBinding Location="/api" ResponseType="JSON">
        <DataService TableName="Product" Type="OData"  />
      </DataBinding>
    </ClientSettings>
    <MasterTableView ClientDataKeyNames="Id" DataKeyNames="Id">
      <Columns>
        <telerik:GridBoundColumn DataField="Id" HeaderStyle-Width="0"
          ItemStyle-Width="0"></telerik:GridBoundColumn>
        <telerik:GridBoundColumn DataField="Name" HeaderText="Name"
          HeaderStyle-Width="150" ItemStyle-Width="150">
          </telerik:GridBoundColumn>
        <telerik:GridBoundColumn ItemStyle-CssClass="gridPrice"
          DataField="Price"
          HeaderText="Price" ItemStyle-HorizontalAlign="Right">
          </telerik:GridBoundColumn>
        <telerik:GridBoundColumn DataField="NumInStock"
          ItemStyle-CssClass="numInStock"
          HeaderText="# in Stock"></telerik:GridBoundColumn>
      </Columns>
    </MasterTableView>
  </telerik:RadGrid>

啟動與即時資料網格

拼圖的最後一塊是顯示即時變化的庫存水準,如產品已發運,並收到的能力。 我要添加一個 SignalR 集線器傳輸更新並提出這些新值的搜索網。 要將 SignalR 添加到我的專案,需要發佈的以下兩個 NuGet 命令:

Install-Package -pre Microsoft.AspNet.SignalR.SystemWeb
Install-Package -pre Microsoft.AspNet.SignalR.JS

這些命令將安裝 IIS Web 服務器內主辦的ASP.NET伺服器元件,並使 JavaScript 用戶端庫提供的 Web 表單。

SignalR 伺服器端元件被稱為一個集線器,,我會通過添加到我的 Web 專案中稱為集線器的資料夾稱為 StockHub 一類定義礦井。 StockHub 需要從 Microsoft.AspNet.SignalR.Hub 類下降。 我定義靜態的 System.Timers.Timer 允許應用程式來類比更改庫存水準。 對於這種類比,每隔 2 秒 (當計時器經過事件處理常式觸發),我會隨機設置一個隨機播放的產品的庫存水準。 產品庫存級別設置後,就會將連接的所有用戶端通知通過調用 setNewStockLevel,所示在用戶端上執行方法圖 11

圖 11 SignalR 集線器伺服器端元件

public class StockHub : Hub
{
  public static readonly Timer _Timer = new Timer();
  private static readonly Random _Rdm = new Random();
  static StockHub()
  {
    _Timer.Interval = 2000;
    _Timer.Elapsed += _Timer_Elapsed;
    _Timer.Start();
  }
  static void _Timer_Elapsed(object sender, ElapsedEventArgs e)
  {
    var products = ProductController._Products;
    var p = products.Skip(_Rdm.Next(0, products.Count())).First();
    var newStockLevel = p.NumInStock + 
      _Rdm.Next(-1 * p.NumInStock, 100);
    p.NumInStock = newStockLevel;
    var hub = GlobalHost.ConnectionManager.GetHubContext<StockHub>();
    hub.Clients.All.setNewStockLevel(p.Id, newStockLevel);
  }
}

為此集線器的資料是可從伺服器訪問,我需要將行添加到 RouteConfig,該值指示該集線器的存在。 通過調用的路線。MapHubs 在 RouteConfig 的 RegisterRoutes 方法中,我完成 SignalR 伺服器端的配置。

下一步,網格需要偵聽這些事件從伺服器。 若要實現此目的,需要添加一些 JavaScript 引用從 NuGet 和 MapHubs 命令從生成的代碼中安裝 SignalR 用戶端庫。 SignalR 服務連接,並公開使用中所示的代碼在用戶端上的 setNewStockLevel 方法圖 12

圖 12 SignalR 用戶端代碼來啟動網格

<script src="/Scripts/jquery.signalR-1.0.0-rc2.min.js"></script>
<script src="/signalr/hubs"></script>
<script type="text/javascript">
  var grid;
  $().ready(function() {
      var stockWatcher = $.connection.stockHub;
      stockWatcher.client.setNewStockLevel = function(id, newValue) {
        var row = GetRow(id);
        var orgColor = row.css("background-color");
        row.find(".
numInStock").animate({
          backgroundColor: "#FFEFD5"
        }, 1000, "swing", function () {
          row.find(".
numInStock").html(newValue).animate({
            backgroundColor: orgColor
          }, 1000)
        });
      };
      $.connection.hub.start();
  })
</script>

在 jQuery 就緒事件處理常式中,我建立引用調用到使用 $ StockHub stockWatcher.connection.stockHub 的語法。 我然後在 stockWatcher 的用戶端屬性上定義的 setNewStockLevel 方法。 此方法使用一些其他 JavaScript 的説明器方法來遍歷網格、 找到具有相應的產品的行和更改的庫存水準與提供的 UI,jQuery 花哨顏色動畫中所示圖 13

The Search Interface with Grid Generated by Web API and Maintained by SignalR
圖 13 由 Web API 生成的網格的搜索介面由 SignalR 和維護

總結

我已經演示了如何生成ASP.NETMVC 專案並添加一個 Web 表單佈局、 協力廠商的 AJAX 控制項和 Web 表單路由到它。 我生成的 UI 與 MVC 模具和啟動的內容與 Web API 和 SignalR。 這個專案從ASP.NET框架的所有四個功能用於展示一個內聚的介面,利用每個元件的最佳功能。 你可以這樣做。 Don不選擇只是一種ASP.NET框架,您的下一個專案。 相反,選擇要使用它們。

Jeffrey T. Fritz   Telerik 的超過 15 年的經驗與開發者福音傳教士作為一種服務模型建立大型多租戶的 Web 應用程式的軟體中。 他是一位 INETA 演講和維護在博客上的 csharpfritz.com。 你可以找到他在 Twitter 上 twitter.com/csharpfritz 和可以達到他在 jeff.fritz@telerik.com

衷心感謝以下技術專家對本文的審閱:Scott靜態 (Microsoft) 和Scott的獵人 (Microsoft)