ASP.NET MVC - 快速入门






4.88/5 (28投票s)
本文重点介绍使用ASP.NET MVC进行数据库优先开发。它使用Northwind数据库,并引导读者开发一个CRUD应用程序,应用注解,自定义样式,并使用jQuery UI Accordion来提供更精致的UI体验。
目录
引言步骤 1) 在VS中创建一个ASP.NET MVC Web应用程序
步骤 2) 将用户配置文件信息移至Northwind数据库
步骤 3) 添加ADO.NET实体数据模型
步骤 4) 为订单录入创建CRUD表单
步骤 5) 添加数据注解
步骤 6) 应用自定义样式
步骤 7) 修改/Order/Index视图的布局
步骤 8) 使用局部视图显示每个订单的行项目
步骤 9) 替换订单和行总计的占位符
步骤 10) 使用jQuery UI Accordion控件
任务摘要
本文中引用的有用链接
历史
引言
ASP.NET MVC——这个并非新生的框架,于2009年发布,标志着与ASP.NET Web Forms范式的一次彻底决裂。我们这些“老派”Web Forms开发者对于这种变化一直行动缓慢。本文主要旨在帮助我完成这一转变,也许它对你们中的一些人也会有所帮助。对我来说,最有效的学习方法是选择一个主题,然后尝试向某人解释它。我发现,当我完成解释时,我对该主题的理解比仅仅阅读该主题或尝试一些实践练习时要深刻得多。
带着这个目标,我希望引导读者和ASP.NET Web Forms开发者通过一些RAD步骤。这个例子侧重于数据库优先开发,因为根据我的经验,这是更常见的场景。我们经常加入一个开发团队,从事一个已有的数据库项目,所以本教程的重点是假设一个现有(无处不在的Northwind)数据库并围绕它进行构建。我将尝试以一种方式构建这些步骤,以便在您尝试我们的示例后,能够轻松地在您自己的数据库上重复这些操作。如果您对代码优先开发示例感兴趣(网络上大多数示例似乎都这样做),请参阅本文末尾标题为“Entity Framework Code First Development入门”的链接。
为了提高效率,我将避免冗长的理论讲解。然而,由于本文面向Web Forms开发者,让我们偷偷地看一眼ASP.NET MVC和Web Forms之间的一些异同。
相似之处
- 两者都建立在ASP.NET框架之上。
- 它们使用相同的语言(C#、VB.NET和J# - 也可以使用其他语言)
- 两者都使用IIS和ASP.NET请求管道,包括HTTP处理程序和HTTP模块。
- 它们还共享相同的配置框架
- 最后,它们都向用户渲染一个包含JavaScript代码、CSS样式和HTML标记的混合响应。
区别
数据库Schema(模型)和业务逻辑(控制器)与UI组件(视图)紧密耦合 | 基于数据库Schema、业务逻辑和UI组件的分离 |
使用视图状态机制来启用页面内部状态感知 | 鼓励无状态方法,但允许通过ViewData/ViewBag和TempData对象对状态进行一些感知。 |
URL通常主要是基于文件的,它们指向服务器上的一个物理文件。 | URL是基于路由的(控制器和将处理请求的动作(方法)的组合) |
使用母版页(Master pages)在多个页面中提供一致的外观和感觉。 | 使用布局(Layouts)和共享视图(shared views)在整个应用程序中提供一致的外观和感觉。 |
用户控件(User controls)用于封装UI对象内的功能。 | 局部视图(Partial Views)在多个视图中提供一致的功能。 |
服务器控件 - 渲染HTML的服务器端代码 | HTML辅助器(HTML Helpers)提供类似但更基本的功能 |
未来展望?
尽管这些框架大相径庭,但微软一直强调它们并非旨在用MVC取代Web Forms。它们一再重申的目标是营造一个两者并存、共同繁荣的环境。
最近关于ASP.NET未来的公告支持了这一说法。请参阅本文末尾标题为“ASP.NET vNext:.NET在服务器上的未来”的链接。
入门要求
您将需要一个支持Web开发的Visual Studio 2013版本 (vs),Sql Server 2008或更高版本以及Northwind数据库。我假设您已经下载并安装了这些或等效组件,因此不再赘述,让我们开始吧。
步骤 1) 在VS中创建一个ASP.NET MVC Web应用程序
打开Visual Studio,点击“开始页面”上的“新建项目”。在“新建项目”对话框中,选择“ASP.NET MVC 4 Web应用程序”,输入“NorthwindMVC”作为我们应用程序的名称,然后点击“确定”。
这将弹出“新建ASP.NET MVC 4项目”对话框,默认选中“Internet应用程序”。点击“确定”接受默认设置并继续。
几秒钟后,您的应用程序模板生成应该完成,您将看到默认控制器 (HomeController.cs) 定义了三个动作(方法)。
右键单击“解决方案资源管理器”中的“App_Data”文件夹,然后选择“在文件资源管理器中打开文件夹”选项。该文件夹应该会打开,并且请注意它应该是空的。
切换回 Visual Studio 并按 F5 键,查看这个默认应用程序壳所呈现的内容。我们的默认应用程序包含一个带有徽标占位符的页眉,以及开箱即用的五个内部页面链接。
其中第一个是“注册”链接。将鼠标悬停在上面,您会注意到它链接到URL https://:1240/Account/Register。换句话说,此链接将由Account控制器的Register动作处理。请注意,您实例中的URL端口号可能会有所不同。
为简洁起见,我将避免讨论路由(请参阅本文末尾的URL路由链接)以继续应用程序。点击“注册”链接并注册用户“Steven Buchanan”,输入您选择的密码。页眉更改为显示, ...“注册”按钮不再可见,“登录”按钮被“注销”按钮取代。
现在回到我们之前打开的文件资源管理器窗口,您应该会看到一个MDF文件和一个LDF文件。这是存储您的UserProfile信息(即您刚刚注册的用户)的数据库文件。
默认项目自动生成一个SQL数据库来支持开箱即用的用户管理,这不是很酷吗?您可以随意点击“关于”和“联系”链接来查看默认应用程序中包含的其他页面。
步骤 2) 将用户配置文件信息移至Northwind数据库
既然我们已经有了一个计划使用的数据库(Northwind),那么将UserProfile及相关表移到我们的Northwind数据库中是很有意义的。为此,请打开web.config并按照以下模板修改连接字符串“DefaultConnection”:
原始连接字符串
Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-NorthwindMVC-20140518034312;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-NorthwindMVC-20140518034312.mdf
修改后的连接字符串
data source=[yourSQLServer];initial catalog=Northwind;integrated security=True;
编译并运行您的应用程序。由于我们不再使用原始数据库,因此需要再次注册用户“Steven Buchanan”。然后打开Northwind数据库并验证是否已添加了新的UserProfile表并包含新用户。您现在可以删除App_Data文件夹中的MDF文件。
修改连接字符串导致应用程序自动向Northwind数据库添加了五张新表。这些表存储登录信息、用户配置文件和角色。除了注册和登录机制,我们的应用程序还包含一个内置选项,允许用户使用支持OAuth授权标准的外部安全提供商登录(参见本文末尾的OAuth简介链接)。这包括Microsoft、Google、Facebook和Twitter。App_Start文件夹中的AuthConfig类包含用于启用上述提供商身份验证的代码。您可以取消注释RegisterAuth方法中的代码,以启用您希望使用的每个提供商。
注意:Google 最易实现,只需取消注释/App_Start/AuthConfig.cs 中的以下行即可激活:
OAuthWebSecurity.RegisterGoogleClient();
..并重新编译您的应用程序。这是一个一次性过程,涉及将一个新的用户帐户(例如,“Steven Buchanan”、“Nancy Davolio”等)与您的Google凭据关联。
由于我们的代码已经全部编写好,请花一点时间回顾一下 /Model/AccountModels.cs (模型)、/Controllers/AccountController.cs (控制器) 以及 Views/Accounts 文件夹中的视图。点击“登录”按钮(视图文件 = _LoginPartial.cshtml)会调用 Account 控制器的 Login(string) 动作。这是 Login 的默认 (HttpGet) 动作。该动作只是简单地要求 Login 视图在布局/母版页(视图文件 = /Shared/_Layout.cshtml)的主体中渲染。我们将在下一段中遇到另一个 Login 动作 (HttpPost)。
// // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult Login(LoginModel model, string returnUrl) { if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe)) { return RedirectToLocal(returnUrl); } // If we got this far, something failed, redisplay form ModelState.AddModelError("", "The user name or password provided is incorrect."); return View(model); }
一旦用户输入用户名和密码,就会调用Login(LoginModel, string)操作。此操作通过应用于方法的[HttpPost]属性连接到post动词。此方法使用WebSecurity(开箱即用)类来验证用户凭据。假设成功,用户将被重定向到Home/Index视图。上一段中提到的_LoginPartial视图会更新以反映用户的身份验证状态。
步骤 3) 添加 ADO.NET 实体数据模型。
右键单击“模型”文件夹,选择“添加 - ADO.NET实体数据模型”。
在“指定项目名称”对话框中,输入“Northwind”。
在实体数据模型向导中选择“从数据库生成”选项,然后点击“下一步”。
从“选择数据连接”对话框中选择“新建连接”按钮。在“连接属性”对话框中输入您的SQL Server名称并选择“Northwind”作为数据库,然后点击“确定”返回到“选择数据连接”对话框。
最后,将“在Web.config中保存实体连接设置”的默认值更改为“NorthwindDB”(见上图蓝色箭头),然后点击“下一步”按钮。
在“选择您的版本”对话框中,选择“Entity Framework 5.0”,然后点击“下一步”按钮。如果可用,您可以选择6.0,这不会以任何方式影响此示例。如果您希望查看EF 6.0的要求,请参阅本文末尾的链接“从EF 5升级到EF 6”。
从“选择您的数据库对象和设置”对话框中,选择如下所示的“表”。此时您无需包含任何视图、存储过程或函数。输入“Northwind”作为“模型命名空间”,然后点击“完成”按钮以生成模型类文件。请注意,除了下面选择的表对象之外,此过程还会自动包含一个DbContext生成器NorthwindDB,您将需要它来执行涉及数据库的操作,从下面的步骤4开始。
系统可能会提示您重新加载web.config文件,如果发生这种情况,请点击“是”。这通常表示web.config之前的更改可能未保存,您可能需要重新输入您的DefaultConnection(参见上面的步骤2)。
您应该会看到一个数据库图(Northwind.edmx),表示所选表及其之间的关系。该图还包含每个表对象的导航属性。这允许您从任何给定对象透明地访问相关对象。例如,一旦您拥有一个Order对象,您也就拥有该Order的Customer、Employee、Shipper和Order_Details对象。
展开我们刚刚添加的ADO.NET实体数据模型的“模型”文件夹中的EDMX节点,然后展开Northwind.tt节点以确认所有类都已创建。您可以双击任何类以查看其生成的代码。请勿编辑此代码!编辑可能会导致问题,并且如果EDMX文件刷新,任何编辑都将丢失。
步骤 4) 为订单录入创建CRUD表单
按F5编译并运行您的解决方案。这有助于使Intellisense了解作为我们模型一部分添加的新类。
右键单击“解决方案资源管理器”中的“控制器”节点,然后选择“添加 - 控制器”。
在“添加控制器”对话框中,输入“OrderController”作为控制器名称,选择模板“具有读写操作和使用Entity Framework的视图的MVC控制器”(默认选择)。选择“Order”作为我们的模型类,“NorthwindDB”作为我们的数据上下文类。
点击“添加”按钮,这将生成创建、检索、更新和删除订单数据所需的所有代码。您可以通过按Ctrl+F5执行代码来确认这一点。在URL中添加“/Order”以调用OrderController的默认操作(Index() - 列出所有订单详细信息)。
注意:由于输出中有数千行,这可能需要一些时间才能在浏览器中渲染,特别是如果您选择在调试模式下运行。您可以随意查看下图屏幕片段中突出显示的“编辑”、“详细信息”和“删除”选项。它可能不美观,但功能齐全。请注意,日期格式不必要地包含了时间部分。我们将在下面的步骤5中处理这个问题和其他格式瑕疵。
同样的流程可以用于Employee、Customer以及您选择的其他表。如果这是您自己的数据库而非Northwind,这些步骤可以轻松地用于重复上述操作并获得相同的结果。我们已经走了很长一段路,但还没有编写一行C#代码、HTML标记或CSS样式。
是时候添加一些代码来帮助我们继续探索ASP.NET MVC生态系统了。让我们从一些微小的更改开始,以帮助我们避免在反复修改和测试示例应用程序时进行一些输入。打开 /Views/Shared/_Layout.cshtml,并修改 <header> 内的 <ul>...</ul> 标签内的 HTML ActionLink 项目,如下所示。这应该会隐藏“关于”和“联系”选项,并向菜单添加一个“订单”链接。
@Html.ActionLink("Home", "Index", "Home") @*@Html.ActionLink("About", "About", "Home") @Html.ActionLink("Contact", "Contact", "Home")*@ @Html.ActionLink("Orders", "Index", "Order")
由于数据库包含数千个订单,按员工限制订单会更有意义。我们可以通过修改OrderController的Index()操作来过滤订单,使用户名与订单关联的员工姓名匹配。这就是我们在步骤1中注册为Steven Buchanan的原因。当然,这将要求每个员工注册的用户名与他们的员工姓名匹配。在实际场景中,我们会将员工与用户配置文件记录关联起来,并从配置文件中获取员工姓名或ID,然后使用它。目前我们倾向于方便!打开OrderController并将Index操作代码替换为下面的代码片段。
public ActionResult Index() { var orders = db.Orders.Include(o => o.Customer) .Include(o => o.Employee).Include(o => o.Shipper); List<Order> orderRows = new List<Order>(); foreach (Order order in orders) { string employeeName = String.Format("{0} {1}", order.Employee.FirstName.Trim() , order.Employee.LastName.Trim()); if (employeeName == User.Identity.Name) orderRows.Add(order); } return View(orderRows); }
按Ctrl+F5编译并启动应用程序。当/Home/Index视图渲染时,以Steven Buchanan身份登录(如果尚未登录),然后点击菜单上的新“订单”链接。我们的/Order/Index视图应该只显示当前登录员工所拥有的订单子集。您可以随意注册另一个员工账户,并确认他们的订单按预期正确显示。
花几分钟时间回顾OrderController中其余动作生成的代码。请注意,所有修改数据库的动作都为HttpGet和HttpPost调用提供了单独的重载变体。有关解释,请参阅本文末尾链接中的HttpGet与HttpPost。
步骤 5) 添加数据注解
完成了基本的CRUD操作后,现在让我们着手改进我们从Visual Studio应用程序模板继承的默认外观和感觉。导航回 /Order/Index 视图,注意列标题是来自Northwind数据库表中相应的列名。作为第一步,我们希望自定义这些标题。这通常通过模型类中的数据注解来实现。数据注解是放置在模型中字段定义上方的标签。这些标签设置了它们所应用的字段的显示和验证特性。以下示例显示了应用于Order对象中ShipName成员的数据注解。第一个标签指定了ShipName字段在任何地方显示时应用的标签,第二个标签指定了一个验证约束,该约束在执行包含该成员的任何输入表单的提交操作之前自动应用。
[Display(Name = "Ship To")] [MaxLength(256, ErrorMessage = "Name cannot exceed 256 characters")] public string ShipName { get; set; }
通过使用数据注解,我们能够对整个应用程序中的数据成员进行一致的处理,这也省去了我们编写最常见的验证例程的麻烦。有关更多详细信息,请参阅本文末尾的MVC数据注解链接。不幸的是,我们无法将这些注解添加到我们的模型类中。由于这些类是根据数据库自动生成的,未来在数据库发生某些更改后,我们可能需要在某个时候重新生成它们。这样的操作将有效地覆盖对这些类所做的任何更改。我们可以通过为模型创建一个元数据类来解决此不便。我们可以通过右键单击“模型”文件夹并添加一个新类来完成此操作。
将此类命名为 ModelMetadata.cs。删除类文件中的默认代码,并插入以下代码替换它。请注意,第二行插入了一个对支持数据注解的类的引用。
using System; using System.ComponentModel.DataAnnotations; namespace NorthwindMVC.Models { #region Connect Base model classes to metadata [MetadataType(typeof(OrderMetaData))] public partial class Order { } #endregion /// <summary> /// Class used to define metadata attributes for Order class. /// </summary> public class OrderMetaData { public int OrderID; public string CustomerID; public Nullable<int> EmployeeID; [Display(Name="Order date")] [Required(AllowEmptyStrings=false,ErrorMessage="Order date is required")] [DataType(DataType.Date,ErrorMessage="Order data must be a valid date") , DisplayFormat(DataFormatString="{0:MM/dd/yyyy}" , ApplyFormatInEditMode=true)] public Nullable<System.DateTime> OrderDate; /// <summary> /// Data annotation for Order Date make it required & validate the date value /// Required Date make it optional but validate the date value if entered /// </summary> [Display(Name="Required Date")] [DataType(DataType.Date,ErrorMessage="Required date must be a valid date") , DisplayFormat(DataFormatString="{0:MM/dd/yyyy}" , ApplyFormatInEditMode=true)] public Nullable<System.DateTime> RequiredDate; [Display(Name="Ship Date")] [DataType(DataType.Date,ErrorMessage="Ship date must be a valid date") , DisplayFormat(DataFormatString="{0:MM/dd/yyyy}" , ApplyFormatInEditMode=true)] public Nullable<System.DateTime> ShippedDate; public Nullable<int> ShipVia; public Nullable<decimal> Freight; [Display(Name="Ship To")] [MaxLength(256,ErrorMessage="Name cannot exceed 256 characters")] public string ShipName; [Display(Name="Mailing Address")] [MaxLength(256,ErrorMessage="Name cannot exceed 256 characters")] public string ShipAddress; [Display(Name="City")] [MaxLength(10,ErrorMessage="Name cannot exceed 100 characters")] public string ShipCity; [Display(Name="Region")] public string ShipRegion; [Display(Name="Zip")] public string ShipPostalCode; [Display(Name="Country")] [MaxLength(100,ErrorMessage="Name cannot exceed 100 characters")] public string ShipCountry; } }
代码中“连接基模型类到元数据”区域内的部分声明了一个新的类OrderMetaData,并将其注解为Order类的元数据类。其余代码包含OrderMetaData类的定义。通过将数据注解移到单独的文件中,我们确保当基类在数据库更改后重新生成时,它们不会被覆盖。按Ctrl+F5重新编译并执行应用程序。导航到/Order/Index视图,列标题应反映上述元数据类中数据标签指定的值。日期列的日期值也按照定义进行了格式化。
请注意,上面的示例代码仅为Order类提供了数据注解。我们排除了Customer、Employee和其他表的数据注解,以减少此处所示代码的长度。您可以随意添加缺少的 数据注解作为学习练习。本文附带的完整解决方案包含所有基类的数据注解。
步骤 6) 应用自定义样式
/Order/Details 视图没有任何样式,并且由于默认的标题和值交替显示方式,可读性不佳。让我们应用自定义样式来提高此视图的可读性。我们通过右键单击“解决方案资源管理器”中的“内容”文件夹,然后选择“添加 - 样式表”,如下面的屏幕截图所示。输入“SiteCustom.css”作为项目名称,然后单击“确定”。 |
![]() |
用以下样式替换默认内容: .display-label { font-weight: bold; } 然后我们修改 App_Start/BundleConfig.cs,如下所示。注意,只插入粗体代码片段。其余代码仅用于帮助确定代码插入位置 bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css" , "~/Content/sitecustom.css")); 按Ctrl+F5并导航到任何订单的/Order/Details视图,查看样式更改。 |
![]() |
步骤 7) 修改/Order/Index视图的布局
现在我们已经了解了样式创建的基础知识,让我们来完成一个更具雄心的任务。在步骤4中生成的默认(/Order/Index)视图有点笨拙。平坦的表格布局需要大量的水平和垂直滚动,这在UI设计方面可能不是最优的。
我们可以修改HTML标记并添加一些CSS样式来实现另一种布局。我们首先通过将其包装在@*...*@标签内来注释掉Index视图中的整个表格。请注意,我更喜欢将注释的表格封装在标签内。这是可选的,它允许我在VS编辑器中折叠注释的代码。我们可以在完成替代布局后将其删除。
a) 添加新的HTML标记
复制以下标记,并将其粘贴到包含创建操作链接的 <p>...</p> 标签下方,以及原始(已注释)表格标记的上方。保存 Index 视图并导航到 /Order/Index 视图。假设您仍然以 Steven Buchanon 身份登录,您应该会在视图中看到更改。
<h3> <div class="tablecontainer"> <div class="row"> <div class="order customer cell left3"> <text>@Html.DisplayNameFor(model => model.Customer.CompanyName)</text> </div> <div class="order date cell middle3"> <text>@Html.DisplayNameFor(model => model.OrderDate)</text> </div> <div class="order amount cell right3"> <text>Order Total</text> </div> </div> </div> </h3> @foreach (var item in Model) { <h3> <div class="tablecontainer margin10"> <div class="row"> <div class="order customer cell left3"> @Html.DisplayFor(modelItem => item.Customer.CompanyName) </div> <div class="order date cell middle3"> @Html.DisplayFor(modelItem => item.OrderDate) </div> <div class="order amount cell right3"> **999** </div> </div> </div> </h3> <div> <p> <div class="tablecontainer"> <div class="row"> <div class="cell left"> <div> <span class="bold">@Html.DisplayNameFor(model => model.RequiredDate): </span>@Html.DisplayFor(modelItem => item.RequiredDate) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShippedDate): </span>@Html.DisplayFor(modelItem => item.ShippedDate) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.Shipper.CompanyName): </span>@Html.DisplayFor(modelItem => item.Shipper.CompanyName) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.Freight): </span>@Html.DisplayFor(modelItem => item.Freight) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipName): </span>@Html.DisplayFor(modelItem => item.ShipName) </div> </div> <div class="spacer"> </div> <div class="cell right"> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipAddress): </span>@Html.DisplayFor(modelItem => item.ShipAddress) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipCity): </span>@Html.DisplayFor(modelItem => item.ShipCity) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipRegion): </span>@Html.DisplayFor(modelItem => item.ShipRegion) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipPostalCode): </span>@Html.DisplayFor(modelItem => item.ShipPostalCode) </div> <div> <span class="bold">@Html.DisplayNameFor(model => model.ShipCountry): </span>@Html.DisplayFor(modelItem => item.ShipCountry) </div> </div> </div> </div> </p> </div> }
b) 添加CSS样式以配合标记
标记本身并不能完成任务,我们需要为与标记关联的类设置样式,以推进我们的改造。复制下面的CSS并将其粘贴到/Content/SiteCustom.css中现有样式的末尾。保存CSS文件并刷新/Order/Index视图。这将带来更干净、响应更灵敏的外观和感觉。
#accordion { width: 98%; } /*Ref.: http://snook.ca/archives/html_and_css/getting_your_di*/ .tablecontainer { display: table; width: 100%; } .row { display: table-row; } .cell { display: table-cell; font-size: .75em; } .left3, .right3, .middle3 { width: 33%; } .left, .right { width: 49%; background-color: #EFEEEF; border-radius: 4px; border: 1px solid #556b2f; padding-left: 1em; } .single { width: 100%; background-color: #EFEEEF; border-radius: 4px; border: 1px solid #556b2f; padding-left: 1em; } .single th { padding-left: 1em; } .single td { padding-left: 2em; } .spacer { width: 2%; } .bold { font-weight: bold; } .margin10 { margin-left: 20px; } .alignright { text-align: right; } .tableinner { display: table; width: 100%; border: 1px; margin-top: 2px; } .rowinner { display: table-row; } .cellinner { display: table-cell; font-size: .75em; }
步骤 8) 使用局部视图显示每个订单的行项目
“局部视图(Partial View)是可重用的内容和代码片段,可以嵌入到另一个视图中,从而提高站点的可用性,同时减少重复代码。”(摘自一篇名为《ASP.NET MVC 3中的局部视图》的帖子。请参阅本文末尾的链接)。它旨在通过在应用程序的多个视图中使用相同的视图片段来减少重复并提高一致性。如引言中所述,它相当于Web Forms域中的用户控件。
我们将创建的局部视图显示给定订单的所有项目。
a) 生成订单详情局部视图
右键单击“/Views/Order”文件夹,选择“添加 - 视图”。
在“添加视图”对话框中,键入“OrderDetailsPartial”作为视图名称。
勾选“创建强类型视图”复选框,并选择“Order_Detail”作为您的模型类。
将脚手架模板值保持原样(空)。
勾选“创建为局部视图”复选框,然后点击“添加”按钮完成此视图的添加。
新视图应该除了下面显示的一行之外是空的。
@model NorthwindMVC.Models.Order_Detail
上面一行我们需要做一项更改。我们将传递的不是一个Order_Detail项目(这是“添加视图”模板假定的默认值),而是一个Order_Detail类型的集合,因为一个订单通常包含多个项目。为了适应这一点,我们通过将其包装在ICollection接口中来修改上面一行,如下所示。
@model ICollection<NorthwindMVC.Models.Order_Detail>
b) 将标记插入局部视图
复制并粘贴以下标记到@model声明下方。此标记将渲染它接收到的Order_Detail集合中的标题和每个行项目。
<div class="tableinner"> <div class="rowinner"> <div class="cellinner single"> <table> <tr> <th class="bold">@Html.DisplayNameFor(model => Model.FirstOrDefault().Product)</th> <th class="bold">@Html.DisplayNameFor(model => Model.FirstOrDefault().Quantity)</th> <th class="bold">@Html.DisplayNameFor(model => Model.FirstOrDefault().UnitPrice)</th> <th class="bold">@Html.DisplayNameFor(model => Model.FirstOrDefault().Discount)</th> <th class="bold">Subtotal</th> </tr> @foreach (NorthwindMVC.Models.Order_Detail line in Model) { <tr> <td>@line.Product.ProductName</td> <td class="alignright">@line.Quantity</td> <td class="alignright">@line.UnitPrice</td> <td class="alignright">@line.Discount</td> <td class="alignright">**999**</td> </tr> } </table> </div> </div> </div>
c) 在主视图页面中调用局部视图
在/Order/Index中插入对新创建的局部布局(OrderDetailsPartial)的调用,如下所示。请注意传递给局部布局的可选第二个参数,它表示订单中的项目集合。这将覆盖默认传递的Order对象。
@Html.Partial("OrderDetailsPartial", item.Order_Details)
将其放置在最后一个tablecontainer div的结束标签之后,以及结束段落(</p>)之前,如下所示。
保存所有文件并在浏览器中刷新/Order/Index视图以查看每个订单的行项目。
步骤 9) 替换订单和行总计的占位符
您可能已经注意到您刚刚编辑的标记中有几个占位符(**999**)。其中一个出现在/Order/Index视图中,另一个包含在OrderDetailsPartial视图中。这些位置需要显示的值需要计算。虽然这种计算可以在视图中进行,但将此处理委托给控制器会更好。
a) 将辅助方法插入OrderController
首先,复制下面的两个方法并将它们添加到OrderController类中。这是两个静态方法,将由替换上一段中提到的两个占位符的代码调用。
static public string getOrderTotal(ICollection<Order_Detail> orderDetails) { decimal orderTotal = 0; foreach (Order_Detail item in orderDetails) { orderTotal += item.UnitPrice * item.Quantity * ((decimal)(1 - item.Discount)); } string returnVal = string.Format("{0:C2}", orderTotal); return returnVal; } static public string getOrderDetailTotal(Order_Detail item) { decimal itemTotal = 0; itemTotal = item.UnitPrice * item.Quantity * ((decimal)(1 - item.Discount)); string returnVal = string.Format("{0:C2}", itemTotal); return returnVal; }
b) 替换/Order/Index视图中的占位符
现在,用对我们刚刚添加到OrderController类中的一个方法的调用来替换/Order/Index中的占位符。注意,我已包含相关的<div>..</div>标签,以帮助识别对getOrderTotal方法的调用位置。您应该只复制标签之间的行并替换占位符**999**。这是对计算订单总额的方法的调用。
<div class="order amount cell right3"> @NorthwindMVC.Controllers.OrderController.getOrderTotal(item.Order_Details) </div>
c) 替换局部视图中的占位符
接下来,用对 getOrderDetailTotal() 方法的调用替换 OrderDetailsPartial 中的占位符(**999**)。再次强调,我已包含伴随的 <td>...</td> 标签,以帮助识别对 getOrderDetailTotal() 的调用位置。请勿包含 <td> 标签,因为它们已存在于现有标记中。此调用将计算每个行项目的项目总计。
<td class="alignright">@NorthwindMVC.Controllers.OrderController.getOrderDetailTotal(line)</td>
保存所有文件,然后按“F5”启动应用程序。以员工身份登录后,导航到/Order/Index视图。您现在应该会看到计算出的订单总额和每个行项目的小计,如下面的屏幕截图所示。
步骤 10) 使用jQuery UI Accordion控件
我们的UI在最初自动生成的布局上取得了显著改进。例如,我们消除了早期布局所需的水平滚动。不幸的是,我们确实有大量数据,视图可以进一步调整。以下控件是管理大型数据集的流行选项。
- jQuery UI 手风琴 (https://jqueryui.jqueryjs.cn/accordion/),
- Trirand的jqGrid(http://trirand.com/blog/jqgrid/jqgrid.html)
在我们的示例中,我们将使用手风琴(accordion)来包装我们的订单对象,并简化用户与UI的交互。这个任务非常简单,因为我们已经完成了启用手风琴所需的大部分工作。我们复制到/Order/Index视图中的标记已经准备好包装在手风琴内部。
a) 在视图中插入手风琴div标签和JavaScript调用
让我们首先在/Order/Index视图中插入所需的标记。注意,不要插入下面显示的@foreach代码块。@foreach代码块作为手风琴
我们还需要添加一点(技术上说,一行 :)) JavaScript,如下所示。这就是激活手风琴的部分。
<div id="accordion"> @foreach (var item in Model) { <h3>...</h3> <div>...</div> } </div> <script type="text/javascript"> window.onload = function () { $("#accordion").accordion({ autoHeight: 'false', heightStyle: 'content', collapsible: true, active: false }); } </script>
b) 在我们的应用程序中激活jQuery UI组件
现在我们需要确保我们的项目中包含了 jQuery UI。虽然 Visual Studio 会自动为 ASP.NET MVC 应用程序包含 jQuery,但它并没有完全激活 jQuery UI 组件——代码可用,但未链接到我们的应用程序中。我们需要完成最后几个步骤来使 jQuery UI 可用。我们通过编辑 /Shared/_Layout 视图文件来完成此操作。这相当于 ASP.NET Webforms 的 Master 文件。让我们首先打开此文件并插入两行。第一行将 jQuery UI CSS 文件附加到我们的应用程序。请注意,下面显示的第一行和第三行已存在于我们的标记中,此处仅作为插入 jQuery UI CSS 行位置的指南。这通过将下面显示的第二行插入到 /Shared/_Layout 文件的 <head> 标签内部来完成。
@Styles.Render("~/Content/css") @Styles.Render("~/Content/themes/base/css") @Scripts.Render("~/bundles/modernizr")
同样,jQuery UI JavaScript文件必须插入到/Shared/_Layout文件中结束的</body>标签之前。再次强调,不要插入下面显示的第一行和第三行,它们已经存在。复制下面的第二行并将其粘贴到第一行和第三行之间。
@Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryui") @RenderSection("scripts", required: false)
下面的屏幕片段显示了最终结果。一些标记部分已被折叠(由边距中的+号表示),以将注意力集中在此练习部分中的新增内容。
c) 使用 NuGet 更新 jQueryUI 库(如果需要)
最后,我们需要确保我们的 jQuery UI 版本不是太旧。检查 /Scripts 文件夹中 jquery-ui-
工具 - 库包管理器 - 管理解决方案的 NuGet 包
...从VS菜单调出“管理NuGet包”对话框。找到jQuery UI(组合库),选择它并点击“更新”按钮以更新应用程序中的jQuery UI文件。请参阅下面的屏幕截图以供参考。
保存所有文件并在浏览器中刷新/Order/Index视图。我们应该会看到一个折叠视图,其中显示一个手风琴,仅显示客户、订单日期和总计。点击任何订单都应显示相关的订单和行项目详细信息。
我们可以继续,但这已经是一篇很长的文章了,这是一个合理的停止点。如果有特别感兴趣的增强功能,请随时在评论中提及,我将尝试进行后续跟进,解决常见请求。感谢您的时间和关注。
任务摘要
步骤 1) 在VS中创建一个ASP.NET MVC Web应用程序在VS中创建一个名为NorthwindMVC的ASP.NET MVC Web应用程序(Internet应用程序)。
步骤 2) 将用户配置文件信息移至Northwind数据库
修改web.config中的连接字符串“DefaultConnection”为:
数据源=[您的SQL服务器];初始目录=Northwind;集成安全=True;
运行应用程序,注册新用户“Steven Buchanan”
步骤 3) 添加ADO.NET实体数据模型
添加一个名为“Northwind”的ADO.NET实体数据模型。
选择“从数据库生成”并输入值以连接到您的Northwind数据库。
当提示时,包括Categories、Customers、Employees、Order Details、Orders、Products、Shippers和Suppliers。
步骤 4) 为订单录入创建CRUD表单
添加一个名为“OrderController”的新控制器,选择“具有读/写操作和视图的MVC控制器,使用Entity Framework”作为模板。
选择“Order”作为模型类,并输入您的数据上下文类,在我们的示例中是NorthwindDB。
修改/Order/Index操作,按登录用户(员工 = Steven Buchanan)限制订单
步骤 5) 添加数据注解
添加ModelMetadata类,输入代码以连接基类到元数据类。
声明元数据类并为基类插入数据注解。
步骤 6) 应用自定义样式
将自定义样式表“SiteCustom.css”添加到项目中。
步骤 7) 修改/Order/Index视图的布局
用提供的标记替换/Order/View中的表格标记。
将提供的CSS样式附加到SiteCustom.css
步骤 8) 使用局部视图显示每个订单的行项目
添加局部视图“OrderDetailsPartial”,为模型类选择Order_Detail。
将脚手架模板保留为空,并勾选局部视图选项。
将视图模型声明更改为 ICollection
用提供的标记替换默认标记
使用Html.Partial辅助器将局部视图插入/Order/Index
步骤 9) 替换订单和行总计的占位符
将提供的辅助方法插入OrderController。
将/Order/Index中的占位符替换为对辅助方法getOrderTotal()的调用。
将/Order/OrderDetailsPartial中的占位符替换为对辅助方法getOrderDetailTotal()的调用。
步骤 10) 使用jQuery UI Accordion控件
将/Order/Index中的标记用div (id='accordion') 包裹
将提供的初始化手风琴的JavaScript代码插入到/Order/Index视图中。
修改/Shared/_Layout视图,插入提供的行以激活jQuery UI组件。
如有必要,使用NuGet包管理器将jQuery UI版本更新到1.10。
按Ctrl+F5执行应用程序。
本文中引用的有用链接
Asp.Net MVC框架的简史(Shailendra Chauhan)
ASP.NET MVC框架的版本历史,包括每个版本的功能列表。
Entity Framework开发方法(MSDN)
对数据库优先、模型优先和代码优先开发方法的探讨。
Entity Framework Code First开发入门 (Tom Dykstra)
使用Contoso University示例Web应用程序演示如何使用Entity Framework 6和Visual Studio 2013创建ASP.NET MVC 5应用程序。本教程使用Code First工作流。
ASP.NET vNext:.NET在服务器上的未来(Xinyang Qiu)
微软.NET Web开发和工具团队关于ASP.NET未来的公告。
URL路由(Scott Guthrie)
深入探讨ASP.NET MVC框架的路由架构。
OAuth 简介 (维基百科)
OAuth是一种开放的授权标准。以上链接详细介绍了该标准涵盖的历史和规范。
从EF 5升级到EF 6 (StackOverflow.com)
StackOverflow上的一个简短问答环节,包括将应用程序从EF5转换为EF6的步骤。
HttpGet 与 HttpPost (Ganesh S Divekar)
一篇博客文章,详细介绍了在给定场景中两者的区别和适用性(何时使用)。
MVC 数据注解(Shailendra Chauhan)
对各种数据注解的简短但近乎完整的解释。
ASP.NET MVC 3 中的局部视图(Suprotim Agarwal)
对局部视图的简要解释及示例。
历史
- 2014年5月19日 - 初稿
- 2014年5月21日 - 添加了任务摘要部分