65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET MVC 2 开发和单元测试应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.68/5 (35投票s)

2010年6月13日

CPOL

13分钟阅读

viewsIcon

136330

downloadIcon

7252

介绍 AJAX、jQuery、JSON、MvcContrib 和 NUnit

引言

学习新软件开发技术的一个挑战是找到一个好的实际示例来指导您完成学习过程。多年来,我见过的绝大多数演示都展示了一个连接到世界闻名的 Northwind 演示数据库的实际示例。通常,这些演示应用程序会展示一个主从订单 GUI,其中使用了少量即插即用的“粘合剂”。我从未从这些应用程序中学到任何东西。真正需要的是更接近真实世界的应用程序,它具有足够的核心功能来促进学习过程。

示例应用

PatientMaintenance.jpg

本文将演示一个示例医疗应用程序,其中包括添加、更新和搜索患者的功能。该应用程序使用 .NET 4.0 实现了 Microsoft 的新 ASP.NET MVC 2 框架。模型-视图-控制器 (MVC) 架构模式将应用程序划分为三个主要组件:模型、视图和控制器。ASP.NET MVC 框架提供了一种替代 ASP.NET Web Forms 模式来创建 Web 应用程序。ASP.NET MVC 框架是一个轻量级、高度可测试的表示框架,它(与基于 Web Forms 的应用程序一样)集成了现有的 ASP.NET 功能,例如母版页和基于成员资格的身份验证。

解决方案和项目

此示例应用程序由四个独立的 Visual Studio 2010 项目组成,它们合并到一个解决方案中,如下所示:

  • MvcWebSite – 这是 MVC Web 项目,其中包含视图和控制器。视图将包含 JavaScript 和 HTML,而控制器将与数据模型和业务逻辑层交互。
  • DataModels - DataModels 项目包含此应用程序所需的数据实体。数据模型被分离到自己的库中,以便在整个企业中共享。
  • CodeProjectBLLCodeProjectBLL DLL 类库将包含所有必需的业务逻辑层和数据访问代码。此层通常称为模型。
  • CodeProjectUnitTests – 此 DLL 类库将包含用于使用 NUnit 测试运行程序测试应用程序的单元测试。

数据模型

应用程序开发通常从定义数据实体和设计数据库开始。对于此示例应用程序,主要数据实体是患者信息。在为该应用程序构建数据库表后,创建了以下类,该类公开底层数据模型。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace DataModels
{
    /// <summary>
    /// Patient Data Model 
    /// </summary>
    public class Patient
    {      
        public long PatientID { get; set; }                 
        public string MedicalID { get; set; }        
        public string SocialSecurityNumber { get; set; }         
        public string FirstName { get; set; }                 
        public string LastName { get; set; }          
        public DateTime DateOfBirth { get; set; }    
        public DateTime EffectiveDate { get; set; }
        public string AddressLine1 { get; set; }                
        public string AddressLine2 { get; set; }        
        public string City { get; set; }        
        public string State { get; set; }               
        public string ZipCode { get; set; }          
        public string PhoneNumber { get; set; }
      
    }   
}

与 MVC 框架一样,与其他框架一样,人们很容易将数据模型类添加到 MVC 项目的Models文件夹下。在企业环境中,您通常需要开发和支持多个支持和共享相同数据及相关数据结构的应用程序。因此,我喜欢为所有数据类创建一个单独的项目,作为一个 DLL 库项目,以便这些类可以在不同应用程序之间跨企业共享。

视图 - 患者维护和搜索

此时,我将介绍从患者维护视图添加患者。PatientMaintenance.aspx 位于Views下的Patient文件夹中。添加新患者需要用户输入患者的名字和姓氏、出生日期、医疗 ID 等。输入这些信息后,用户可以按“保存”按钮。

<h3>Patient Information</h3>
<form method="post" action="./" id="PatientMaintenanceForm">
<table style="background-color: #ebeff2; width: 100%; border:solid 1px #9fb8e9" 
cellpadding="2" cellspacing="2">   
<tr><td> </td></tr>
 
 <tr>
 <td>Medical ID:</td>
 <td style="width:200px"><input name="MedicalID" type="text" id="MedicalID"  /></td>
 <td>Effective Date:</td><td>
 <input name="EffectiveDate" type="text" id="EffectiveDate"
    style="margin:0px 5px 0px 0px" />      
</td><td style="width:40%"> </td></tr> 
 
<tr>
<td>First Name:</td>
<td style="width:200px"><input name="FirstName" type="text" id="FirstName" /></td>      
<td>Last Name:</td>
<td><input name="LastName" type="text" id="LastName"  /></td>
<td style="width:40%"> </td>
</tr>  
 
<tr>
<td>SSN:</td>
<td style="width:200px">
<input name="SocialSecurityNumber" type="text" id="SocialSecurityNumber"  /></td>       
<td>Date Of Birth:</td>
<td><input name="DateOfBirth" type="text" id="DateOfBirth" />
</td>
</tr>         
 
<tr>
<td>Address Line 1:</td>
<td style="width:200px"><input name="AddressLine1" type="text" id="AddressLine1" />
</td>        
<td>Address Line 2:</td>
<td><input name="AddressLine2" type="text" id="AddressLine2" /></td>
</tr>
 
<tr>
<td>City:</td>
<td><input name="City" type="text" id="City"  /></td>        
<td>State:</td>
<td>
<select id="State" name="State">
        <option value=""></option>           
</select>
</td>
</tr>  
 
<tr>
<td>Zip Code:</td>
<td><input name="ZipCode" type="text" id="ZipCode"  /></td>       
<td>Phone Number:</td>
<td><input name="PhoneNumber" type="text" id="PhoneNumber"  /></td>
</tr>  
 
<tr>
<td> </td>         
</tr> 
</table> 
 
<table style="background-color:#D3DCE5; margin-top:10px; width:100% ">
<tr>
<td>
 
<input id="btnSave" type="button" onclick="SavePatient();" value="Save" />    
 
<input id="btnReset" type="button" value="Reset" onclick="ResetPage();" />    
</td>
</tr>                           
</table>       
 
<br />
<div id="DivMessage"> </div>       
</form>
</div>

MVC 的一个优点是它通过控制器层中的UpdateModel命令提供了一种机制,可以将 HTML 表单字段绑定到业务模型对象。在上​​面的 HTML 患者表单中,我为每个文本框都分配了与我的患者数据模型属性匹配的 ID 和名称。当此表单提交到控制器时,控制器将把表单的值绑定并映射到患者对象。

jQUERY

jQuery 是一个快速简洁的 JavaScript 库,可简化 HTML 文档的遍历、事件处理、动画和 Ajax 交互,从而实现快速 Web 开发。jQuery 还具有各种插件和小部件来增强 Web UI 的开发。此示例应用程序使用了两个 jQuery UI 插件:日期选择器和掩码输入插件。我从 jQuery.com 下载了这两个插件,并在应用程序的母版页中引用了脚本文件。jQuery 还提供自定义主题,用于外观和感觉。对于日期选择器,我选择了下载 Pepper Grinder 主题。掩码输入插件允许您格式化日期、电话号码、社会安全号码等,这些功能并非 MVC 和标准 HTML 开箱即用。

<head runat="server">
 
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
link href="../../Content/css/PepperGrinder/jquery-ui-1.8.2.custom.css" 
    rel="stylesheet" type="text/css"  /> 
 
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>  
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.4.2.min.js" type="text/javascript"></script> 
 
<script src="../../Scripts/jquery.maskedinput-1.2.2.min.js"
                 type="text/javascript"></script> 
 
<script src="../../scripts/jquery.ui.core.js" type="text/javascript"></script> 
<script src="../../scripts/jquery.ui.widget.js" type="text/javascript"></script>
<script src="../../scripts/jquery.ui.datepicker.js" type="text/javascript"></script>
 
</head>
<script language="javascript" type="text/javascript">
 
 $(pageReady);           
 
 function pageReady() {
 
    $.ajaxSetup({ cache: false });
          
    formIsDisabled = false;                
     
    $(function () {
        $("#PatientMaintenanceForm #DateOfBirth").mask("99/99/9999");
        $("#PatientMaintenanceForm #EffectiveDate").mask("99/99/9999");
        $("#PatientMaintenanceForm #PhoneNumber").mask("(999) 999-9999");
        $("#PatientMaintenanceForm #SocialSecurityNumber").mask("999-99-9999");

    }); 
 
    $(function () {$("#PatientMaintenanceForm #EffectiveDate").datepicker({ 
              showOn: 'button', 
              buttonImage: '../../content/cal.gif', 
              buttonImageOnly: true });                 
          });                                                                
} 
 
</script>

jQuery 日期选择器

datepicker.JPG

将数据提交到服务器

当用户按下“保存”按钮时,浏览器中会执行SavePatient JavaScript 函数。SavePatient函数使用 jQuery 的 serialize 方法将 HTML 表单字段的值转换为基于传统 HTML 查询字符串表示法的一种表示法。执行 HTML POST 时,会将此string发送到服务器。

<script language="javascript" type="text/javascript"> 
 
function SavePatient() {
  
   formData = $("#PatientMaintenanceForm").serialize();
  
   DisableForm();
   
   setTimeout(PostChangesToServer, 1000); 
          // simulate a delay to the server
 
}

function PostChangesToServer() {
                             
   $.post("/Patient/AddPatient", formData, 
               function (data, textStatus) {
                 PatientUpdateComplete(data);
                 }, "json");
  }

</script>

禁用表单

function DisableForm() { 
     
     formIsDisabled = true;
 
     $('#PatientSearchForm :input').attr('disabled', true);
     $('#PatientSearchForm').css("cursor", "wait"); 
     $('#PatientMaintenanceForm :input').attr('disabled', true);
     $('#PatientMaintenanceForm').css("cursor", "wait");    
  
}

为了将表单数据提交到服务器,应用程序将使用 jQuery 执行一个 AJAX POST 请求到服务器。由于对服务器的调用是异步的,因此用户在提交请求期间仍然可以执行页面上的其他功能,这可能会导致不理想的情况。为防止这种情况,需要禁用表单。大多数网站通常会显示一个进度指示器来通知用户应用程序正在忙碌,并且表单被灰色化。在大多数情况下,我发现这种方法并不理想,也不够美观。在此示例应用程序中,采用了更传统的方法,这种方法在桌面应用程序世界中很常见。应用程序只需将光标更改为沙漏图标,并禁用页面上的按钮和链接。

AJAX POST 到服务器

禁用表单后,将执行 jQuery POST方法。POST方法使用提供的表单字段数据异步调用服务器。引用“/Patient/AddPatient”的 MVC 路由,这基本上是对PatientController类进行调用并执行控制器的AddPatient方法。当请求从服务器返回时,将向回调函数“PatientUpdateComplete”返回一个 JSON 对象。

JSON 是一种轻量级的数据交换格式,用于在客户端和服务器之间交换数据。由于其简单性以及其格式基于 JavaScript 对象字面量,因此它经常在 Ajax 应用程序中使用。

AddPatient 控制器方法

/// <summary>
/// Add Patient
/// </summary>
/// <returns></returns>
public JsonResult AddPatient()
{      
    bool returnStatus;
    string returnErrorMessage;      
      
    List<string> returnMessage; 
  
    PatientBLL patientBLL = new PatientBLL();
    Models.PatientViewModel patientViewModel = new Models.PatientViewModel();          
            
    this.TryUpdateModel(patientViewModel);
  
    Patient patient = patientBLL.AddPatient(
                patientViewModel, 
                out returnMessage, 
                out returnStatus, 
                out returnErrorMessage);
 
    patientViewModel.UpdateViewModel(patient, typeof(Patient).GetProperties());

    patientViewModel.ReturnMessage = returnMessage;
    patientViewModel.ReturnStatus = returnStatus;  
 
    return Json(patientViewModel); 
}

在 MVC 框架中,另一个诱人的做法是将业务逻辑和数据访问代码直接放在控制器类中。我坚信所有业务逻辑和数据访问代码都应驻留在 MVC 项目之外的单独项目中。这有利于在多个业务应用程序之间共享应用程序代码。我也认为控制器类除了作为视图和应用程序其余部分之间的通道或中间人之外,不应该包含其他任何东西。控制器应该只是将视图中的数据绑定到业务对象,然后执行位于 MVC 项目之外类库中的业务逻辑层代码。

Patient View Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DataModels;
using System.Reflection;
using CodeProjectBLL;
 
namespace MVCWebSite.Models
{
    public class PatientViewModel : DataModels.Patient
    {        
        public long CurrentPageNumber { get; set; }     
  
        public long PageSize { get; set; }      
        public long TotalPages { get; set; }
        public long TotalRows { get; set; }
        public List<string> ReturnMessage { get; set; }
        public bool ReturnStatus { get; set; }
        public string SortBy { get; set; }
        public string SortAscendingDescending { get; set; }             
    }  
}

AddPatient方法首先实例化业务逻辑层类和一个PatientViewModel类。PatientViewModel类继承自Patient数据模型,并包含视图所需的其他属性,包括用于从服务器返回状态和消息的属性。

数据绑定

控制器方法中的关键代码行是TryUpdateModel语句。此语句将使用患者维护视图中的值填充PatientViewModel类中的属性。它通过按名称映射数据元素来实现这一点。如前所述,视图中的文本框对象在PatientViewModel中具有相同的属性名称。

然后,AddPatient控制器方法继续调用patientBLL.AddPatient方法,该方法将患者信息插入 SQL Server 数据库,然后返回一个具有一些重新格式化数据的新患者对象。在这种情况下,输入的数据将转换为大写,存储在数据库中,然后返回给控制器。

更新患者视图模型并返回 JSON

patientBLL.AddPatient成功执行后,控制器将执行UpdateViewModel方法。这是一个自定义方法,它使用 .NET 反射将属性从患者对象更新到PatientViewModel中。此功能类似于TryUpdateModel命令,并且基本上是反向按名称映射和更新属性值。

最后,AddPatient控制器方法将PatientViewModel对象作为 JSON 对象返回,该对象可以被视图解析。优点是 MVC 会自动将您的类对象转换为 JSON 对象。

更新视图

<script language="javascript" type="text/javascript">
 
 function PatientUpdateComplete(jsonPatient) {
    
    EnableForm(); 
 
    firstName = $("#PatientMaintenanceForm #FirstName");
    firstName.scrollIntoView(); 
 
    if (jsonPatient.ReturnStatus == true) {
      $("#PatientMaintenanceForm #FirstName").val(jsonPatient.FirstName);
      $("#PatientMaintenanceForm #LastName").val(jsonPatient.LastName);
      $("#PatientMaintenanceForm #AddressLine1").val(jsonPatient.AddressLine1);
      $("#PatientMaintenanceForm #AddressLine2").val(jsonPatient.AddressLine2);
      $("#PatientMaintenanceForm #City").val(jsonPatient.City);
      $("#PatientMaintenanceForm #State").val(jsonPatient.State);
      $("#PatientMaintenanceForm #ZipCode").val(jsonPatient.ZipCode);
      $("#PatientMaintenanceForm #PhoneNumber").val(jsonPatient.PhoneNumber);
      $("#PatientMaintenanceForm #SocialSecurityNumber").val(
                       jsonPatient.SocialSecurityNumber);
      $("#PatientMaintenanceForm #MedicalID").val(jsonPatient.MedicalID);
      $("#PatientMaintenanceForm #DateOfBirth").val(
                       jsonPatient.PatientDateOfBirth);
      $("#PatientMaintenanceForm #EffectiveDate").val(
                       jsonPatient.PatientEffectiveDate);
      $("#PatientMaintenanceForm #PatientID").val(jsonPatient.PatientID);
    } 

    var returnMessage = ""; 
    for ( i=0; i<jsonPatient.ReturnMessage.length; i++ )
    {
        returnMessage = returnMessage + jsonPatient.ReturnMessage[i] + "<br>";
    } 
    if (jsonPatient.ReturnStatus == true) {
          $("#DivMessage").css("background-color", "#ebeff2");
          $("#DivMessage").css("border", "solid 1px #9fb8c9");
          $("#DivMessage").css("color", "#36597f");
          $("#DivMessage").css("padding", "5 5 5 5"); 
    }
    else {
          $("#DivMessage").css("background-color", "#f4eded");
          $("#DivMessage").css("border", "solid 1px #d19090");
          $("#DivMessage").css("color", "#762933");
          $("#DivMessage").css("padding", "5 5 5 5"); 
    }
    $("#DivMessage").html(returnMessage); 
}

如前所述,服务器将 JSON 对象返回给 JavaScript 回调函数PatientUpdateComplete。在上​​面的示例中,jQuery 用于解析 JSON 对象并更新视图中的表单字段,并检查和显示返回的状态信息。基本上,此示例使用 jQuery 进行了往返 AJAX 调用。

function EnableForm() {                
  
    formIsDisabled = false;  
             
    $('#PatientSearchForm  :input').removeAttr('disabled');
    $('#PatientSearchForm').css("cursor", "default");    
    $('#PatientMaintenanceForm  :input').removeAttr('disabled');
    $('#PatientMaintenanceForm').css("cursor", "default");            
 
}    

最后一步是启用 HTML 表单,让用户继续操作。

单元测试

使用新的 ASP.NET MVC 框架的一个主要好处是,您可以实现“关注点分离”。这意味着与 ASP.NET Web Forms 相比,您的应用程序代码现在更易于测试。MVC 控制器取代了 Web Form 的代码隐藏文件。由于控制器是标准的类对象,现在可以使用单元测试工具直接对它们进行单元测试。

为了对这个示例应用程序进行单元测试,我下载了三个开源项目:NUnit、MVCContrib 和 Rhino Mocks。

  • NUnit 是一个开源的单元测试框架,适用于所有 .NET 语言,可以从 http://www.nunit.org 下载。
  • MVCContrib 也是一个 ASP.NET MVC 框架的开源项目。该项目在 MVC 框架之上增加了额外功能。此示例应用程序将使用TestHelper库进行单元测试。从 http://mvccontrib.codeplex.com 下载。
  • Rhino Mocks 是一个用于 .NET 平台的动态 Mock 对象框架。它的目的是通过允许开发人员创建自定义对象的 Mock 实现并通过单元测试验证交互来简化测试。MVCContrib 在使用TestHelper库时会使用此框架。从 http://www.ayende.com/projects/rhino-mocks/downloads.aspx 下载。

为了测试AddPatient控制器方法,我在我的CodeProjectUnitTests项目中创建了一个名为Test_AddPatient的方法,并引用了 NUnit 和 MVCContrib 框架。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MvcContrib.TestHelper;
using MVCWebSite;
using MVCWebSite.Models;
using MVCWebSite.Controllers;
using System.Web.Mvc;
using System.Collections.Specialized;
using System.Configuration;
using DataModels;
  
namespace CodeProjectUnitTests
{
    /// <summary>
    /// Unit Tests
    /// </summary>
    [TestFixture]
    public class UnitTests
    {        
        /// <summary>
        /// Test Add Patient
        /// </summary>
        [Test]
        public void Test_AddPatient()
        {

            TestControllerBuilder builder = new TestControllerBuilder();

            string uniquePatientKey = GenerateUniqueID();
 
            builder.Form["MedicalID"] = uniquePatientKey;
            builder.Form["SocialSecurityNumber"] = uniquePatientKey;
            builder.Form["FirstName"] = "William";
            builder.Form["LastName"] = "Gates";
            builder.Form["AddressLine1"] = "Microsoft Corporation";
            builder.Form["AddressLine2"] = "One Microsoft Way";
            builder.Form["City"] = "Redmond";
            builder.Form["State"] = "WA";
            builder.Form["ZipCode"] = "98052-7329";
            builder.Form["PhoneNumber"] = "(425)882-8080";
            builder.Form["DateOfBirth"] = "10/28/1955";
            builder.Form["EffectiveDate"] = "01/01/1975";
            
            PatientController patientController = 
               builder.CreateController<PatientController>();
 
            JsonResult jsonResult = (JsonResult)patientController.AddPatient();
 
            dynamic jsonData = jsonResult.Data; 
 
            Assert.AreEqual(jsonData.ReturnStatus, true);
            Assert.Greater(jsonData.PatientID, 0);                            
        }         
   } 
}

上述测试方法首先创建一个TestControllerBuilder对象。该对象是MVCContrib库中MvcContrib.TestHelper命名空间的一部分。TestControllerBuilder不仅创建了控制器的实例,还 Mock 掉了HttpContextSession和表单数据。它还提供了对 MVC ViewDataTempData等的访问。

上面的单元测试 Mock 掉了表单数据,创建了控制器,并执行了Patient控制器的AddPatient方法。控制器返回 JSON,测试方法可以使用 NUnit 的断言命令来检查和测试。由于 JSON 对象的属性在编译时是匿名的,因此测试方法将 JSON 对象中的数据声明为动态(如 C# 中所介绍的)。这意味着我可以引用这些属性,但这些属性将在运行时执行时才进行评估或解析。

NUnit 测试运行程序

testrunner2.jpg

CodeProjectUnitTests程序集加载到 NUnit 测试运行程序中,您现在可以执行您的单元测试,直接在 MVC 控制器类上运行,而无需 IIS 或 ASP.NET 开发服务器运行。

附加功能 - 患者搜索

此示例应用程序还包括第二个 HTML 表单,演示患者搜索功能,该功能返回并构建一个包含分页和排序支持的 HTML 网格。

/// <summary>
/// Patient Search
/// </summary>
/// <returns></returns>
public PartialViewResult PatientSearch()
{
      long totalRows;
      long totalPages;
      bool returnStatus;
 
      string returnErrorMessage;
      PatientBLL patientBLL = new PatientBLL();

      Models.PatientViewModel 
             patientViewModel = new Models.PatientViewModel();           

      this.UpdateModel(patientViewModel);
 
      List<Patient> patients = patientBLL.PatientSearch(
                patientViewModel,
                patientViewModel.CurrentPageNumber, 
                patientViewModel.PageSize,
                patientViewModel.SortBy,
                patientViewModel.SortAscendingDescending, 
                out totalRows,
                out totalPages,
                out returnStatus,
                out returnErrorMessage);
 
      ViewData["patients"] = patients;

      patientViewModel.TotalPages = totalPages;
      patientViewModel.TotalRows = totalRows;
 
      ViewData.Model = patientViewModel;
      return PartialView("PatientSearchResults");
 } 

当在视图中点击“搜索”按钮时,通过 jQuery Ajax 调用执行控制器类的PatientSearch方法。业务逻辑层返回一个泛型患者对象列表,这些对象存储在ViewData对象中。ViewData对象是传递应用程序数据到视图的标准方法。

分页功能通过传递当前页码和每页大小的值来控制。业务层仅根据这两个参数返回单个患者对象页面。在渲染完第一页后,用户可以点击下一页链接,该链接将当前页码增加一,以从数据库中获取下一组患者。

在此示例中,没有将 JSON 对象传回视图。这次,返回的是一个部分视图结果。目标是使用名为“PatientSearchResults.ascx”的 MVC 视图用户控件动态生成网格,如下所示:

<table><tbody> <% 
int i = 0;
foreach (var patient in (IEnumerable<DataModels.Patient>)ViewData["patients"])
{ %> 
<tr style="height:25px; color:Black; 
background-color:<%= i++ % 2 == 0 ? "#D3DCE5" : "#ebeff2" %>"> 
<td style="width: 20%">
<%= Html.Encode(patient.MedicalID)%>
</td>
<td style="width: 20%">
<%= Html.Encode(patient.SocialSecurityNumber)%>
</td>
<td style="width: 20%">
<a href="javascript:GetPatientDetails('<%= Html.Encode(patient.PatientID)%>');">
<%= Html.Encode(patient.LastName)%></a> 
</td>
<td style="width: 20%">
<%= Html.Encode(patient.FirstName)%>
</td>
<td style="width: 40%">
</td>
</tr>
<% } %>
</tbody>
</table>

视图用户控件通过遍历ViewData模型对象中的患者泛型列表来生成网格所需的 HTML。所有这些 HTML 然后被传回原始的 JavaScript 调用。

 function PostSearchToServer() {
 
    $.post('/Patient/PatientSearch', formData, function (returnHtml) {
            
          $("#DivPatientSearchResults").html(returnHtml);
            
          currentPageNumber = $("#PatientSearchForm #CurrentPageNumber").val();
          totalPages = $("#PatientSearchForm #TotalPages").val();
            
          if (totalPages > 0) 
              SetPagingLinks(currentPageNumber, totalPages);
                            
          EnableForm(); 
    });
}

当 HTML 从视图用户控件返回时,PatientSearch.ascx”视图用户控件内的DIV标签会被返回的 HTML 动态更新。

<div id="DivPatientSearchResults">
 <% Html.RenderPartial("PatientSearchResults",Model); %>
</div>

对于那些在 Microsoft .NET 出现之前就有经典 ASP 开发经验的人来说,会发现<% %>语法很熟悉。当 MVC 1.0 最初发布时,我并不太愿意回到这种编码风格。但随着我重新熟悉这种语法,我能够继续使用该技术。之后,我将发现限制这种服务器端编码风格的各种方法,例如使用 jQuery 解析 JSON 对象、从类库生成 HTML 或实现您可用的各种备用视图引擎之一。尤其令人印象深刻的是 Spark View Engine(http://sparkviewengine.com)。Spark View Engine 的理念是让 HTML 主导视图的流程,让服务器端代码无缝集成。

MVC 框架的另一个我喜欢的地方是,您可以像 Web Forms 那样在一个页面上有多个表单,而 Web Forms 只允许每页有一个表单。在 Web 页面上拥有多个表单可以让您模拟 Web Forms 类似的 AJAX 更新面板和部分页面回发功能。通过 JavaScript 覆盖 HTML 表单的ACTION属性,您可以创建非常复杂的页面,这些页面可以回发到各种不同的 MVC 控制器和方法。

结论

这只是一个使用 MVC 框架开发 Web 应用程序的入门示例。MVC 框架非常丰富且强大。本文档并未涵盖 MVC 框架的许多领域。MVC 框架还附带各种帮助类,用于生成 HTML 对象。您还可以将属性和过滤器应用于控制器方法,这些方法可以在控制器方法执行之前和之后执行。最终,您将对发送到浏览器的 HTML 拥有更大的控制权,更重要的是,MVC 框架通过应用程序的不同层实现了更大的关注点分离,从而促进了单元测试的自动化。

技术栈

  • Microsoft Visual Web Developer 2010 Express
  • Microsoft ASP.NET MVC 2.0
  • Microsoft .NET C# 4.0
  • Microsoft SQL Server 2008 R2 Express Edition
  • NUnit 2.5.5
  • MVCContrib
  • Rhino Mocks
© . All rights reserved.