TutorialsPoint ASP.Net MVC 教程

来源：https://www.yiibai.com/asp.net_mvc/

ASP.Net MVC教程

原文：https://www.yiibai.com/asp.net_mvc

ASP.NET MVC是微软的一个开源软件。 其Web开发框架结合了MVC(模型 - 视图 - 控制器)体系结构的特性，它是最新的敏捷开发思想和技术以及现有ASP.NET平台的最佳部分。 本教程提供了MVC框架的完整图片，并教您如何使用此工具构建应用程序。

面向读者

本教程专为那些热衷于使用ASP.NET MVC开发最佳应用程序的开发人员而设计。 本教程提供了一个实用的方法来与主题分步程序的例子，这将有助于你在学习和实践中获得的知识。

完成本教程后，您将更好地理解Windows应用程序，并了解如何使用XAML和 C# 使用Windows应用程序执行的操作。

前提条件

为了更好地学习本教程，您需要熟悉Windows编程，还需要了解C#的基础知识。

问题反馈

我们不能保证您在此ASP.NET MVC编程教程中不会遇到任何问题。本教程中的讲解，示例和代码等只是根据作者的理解来概括写出。由于作者水平和能力有限，因此不能保证所有的编写文章和示例均能准确无误。但是如果有遇到任何错误或问题，请反馈给我们，我们会及时纠正以方便后继读者阅读。

易百教程移动端：请扫描本页面底部(右侧)二维码并关注微信公众号，回复："教程" 选择相关教程阅读或直接访问：http://m.yiibai.com 。

ASP.Net MVC简介

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_overview.html

ASP.NET MVC基本上是一个来自微软的Web开发框架，它结合了MVC(模型 - 视图 - 控制器)体系结构的特性，它是最新的敏捷开发思想和技术，以及现有ASP.NET平台的最好部分 。

MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式：

Model(模型)表示应用程序核心(比如数据库记录列表)。

View(视图) 显示数据(数据库记录)。

Controller(控制器) 处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。

ASP.NET MVC不是从零开始构建的东西。它是传统ASP.NET Web窗体的完全替代方案。它建立在ASP.NET的顶层，因此开发人员在构建MVC应用程序时几乎可以享受/使用到所有的ASP.NET功能。如下架构图所示 -

ASP.Net MVC历史

作为.Net Framework版本1.0的一部分，ASP.NET 1.0于2002年1月5日发布。 那时，许多开发者很容易把ASP.NET和Web Forms想成是一回事。 但是ASP.NET总是支持两层抽象 -

System.Web.UI - Web窗体层，包括服务器控件，ViewState等等。

System.Web - 提供基本的Web堆栈，包括模块，处理程序，HTTP堆栈等。

在2007年ASP.NET MVC宣布的时候，MVC模式正在成为构建Web框架的最流行的方式之一。

2009年4月，ASP.NET MVC源代码在Microsoft公共许可证(MS-PL)下发布。 “ASP.NET MVC框架是一个轻量级，高度可测试的演示框架，与现有的ASP.NET功能集成在一起。

其中一些集成功能是母版页和基于成员资格的认证。MVC框架在System.Web.Mvc程序集中定义。

2012年3月，微软公司以开源许可证(Apache License 2.0)发布了部分Web栈(包括ASP.NET MVC，Razor和Web API)。 ASP.NET Web窗体不包含在这个计划中。

为什么要ASP.NET MVC？

微软决定创建自己的MVC框架来构建Web应用程序。 MVC框架只是建立在ASP.NET之上。 在使用ASP.NET MVC构建Web应用程序时，不会出现状态幻想，不会出现页面加载和页面生命周期等问题。

ASP.NET MVC的另一个设计目标是在整个框架的所有方面都是可扩展的。所以当我们谈论视图时，视图必须由特定类型的视图引擎呈现。 默认的视图引擎仍然可以采取一个ASPX文件。 但是如果不喜欢使用ASPX文件，那么可以使用别的东西，并插入自己的视图引擎。

在MVC框架内有一个组件将实例化控制器。你可能不喜欢MVC框架实例化控制器的方式，可能想要自己处理这个工作。 所以，在MVC中有很多地方可以注入自己的定制逻辑来处理任务。

ASP.NET MVC的好处

以下是使用ASP.NET MVC的好处 -

通过将应用程序划分为模型，视图和控制器，可以更轻松地管理复杂性。

启用对呈现的HTML的完全控制，并提供一个清晰的关注点分离。

直接控制HTML也意味着更好的可访问性来实现对不断发展的Web标准的遵从。

便于为现有的应用程序添加更多的交互性和响应能力。

为测试驱动开发(TDD)提供更好的支持。

适用于大型开发团队支持的Web应用程序以及需要对应用程序行为进行高度控制的Web设计人员(方便前端工程师的设计工作)。

ASP.Net MVC模式

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_pattern.html

MVC(模型 - 视图 - 控制器)设计模式实际上已经存在了几十年，并且已经用于许多不同的技术。 从Smalltalk到C++到Java的一切，现在C#和.NET使用这种设计模式来构建用户界面。

以下是MVC模式的一些显着特点 -

最初它在1979年被命名为：Thing-Model-View-Editor，然后它被简化为：Model-View-Controller。

这是一种分离应用程序中的问题(例如，将数据访问逻辑与显示逻辑分离)的强大而优雅的手段，并且非常适合于Web应用程序。

其明确的关注点分离确实为应用程序的设计增添了一点额外的复杂性，但非凡的好处超过了额外的工作量。

MVC体系结构模式将应用程序的用户界面(UI)分为三个主要部分。

模型(Model) - 描述正在使用的数据以及业务逻辑的一组类。

视图(View) - 定义如何显示应用程序的用户界面。 这是一个纯粹的HTML，它决定了UI的外观。

控制器(Controller) - 一组处理来自用户的请求，整体应用程序流程和特定于应用程序的逻辑的类。

MVC背后的想法

MVC背后的想法是：将拥有一个名为视图(View)的组件，该组件负责呈现用户界面，不管它是HTML还是桌面应用程序上的UI小部件。

该视图与模型交互通信，该模型包含视图需要显示的所有数据。视图通常就没有太多的逻辑。

在一个Web应用程序中，视图可能根本没有任何关联的代码。 它可能只是HTML，然后是从模型中获取数据片段的表达式，并将它们插入到视图中的正确位置来构建HTML模板并显示给用户。

组织的控制器就是一切。 当一个HTTP请求到达MVC应用程序时，该请求被路由到一个控制器，然后由控制器来与数据库，文件系统或模型进行通信。

ASP.Net MVC开发环境配置

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_environment_setup.html

MVC开发工具包含在Visual Studio 2012及更高版本中。 它也可以安装在Visual Studio 2010 SP1 / Visual Web Developer 2010 Express SP1上。 如果您使用Visual Studio 2010，则可以使用 Web Platform Installer 安装MVC 4

Microsoft提供了Visual Studio的免费版本，该版本还包含SQL Server，可以从 http://www.visualstudio.com 下载，在本教程中使用的是 Visual Studio 2017的社区版本。

Visual Studio 2017社区版本(下载： http://www.visualstudio.com/downloads/ )作为开发工具。

请注意，需要有效的互联网连接才能安装快速版。

下载完成后，按照安装软件的常用方法，双击软件包，启动安装界面，同意协议，选择安装目录，然后在线下载安装，至到完成。

在安装完成后，启动Visual Studio 2017后的工作界面如下 -

ASP.Net MVC入门程序

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_getting_started.html

在本章中，我们来看看一个ASP.NET MVC的简单工作示例。在这里构建一个简单的Web应用程序。要创建一个ASP.NET MVC应用程序，首先需要启动Visual Studio 2017 社区版本，它包含创建，测试和部署一个MVC Framework应用程序所需的所有功能。

创建ASP.Net MVC应用程序

以下是使用Visual Studio 中提供的项目模板创建项目的步骤。

第1步 - 打开Visual Studio 2017 。点击菜单：文件 -> 新建 -> 项目 选项。然后弹出一个新的项目对话框。
第2步 - 在左侧窗格中，选择模板:已安装 -> Visual C# -> Web。
第3步 - 在中间窗格中，选择“ASP.NET Web应用程序”。如下图所示 -

第4步 - 在名称字段中输入项目名称：FirstMVCApp，然后单击 确定 以继续。将看到下面的对话框，要求您设置ASP.NET项目的初始内容。

第5步 - 为了简单起见，请选择“空” 选项，并在“为以下项添加文件夹和核心引用” 部分中选中 “MVC” 复选框，点击 确定。

它将使用最少的预定义内容来创建一个基本的MVC项目。
在Visual Studio创建项目之后，将在“解决方案资源管理器” 窗口中看到许多文件和文件夹。

这样，我们已经从一个空的项目模板创建了ASP.Net MVC项目，所以目前应用程序不包含任何可以运行的东西。

第6步 - 运行这个应用程序从菜单：调试 -> 开始执行(不调试)选项，应该会看到一个404未找到错误的页面提示。如下图所示 -

默认浏览器是FireFox，但是可以从工具栏中选择已安装的任何浏览器。如下图所示 -

添加控制器

要解决上面的“404 Not Found”错误，我们需要添加一个控制器来处理所有传入的请求。

第1步 - 添加控制器，请右键单击项目：FirstMVCApp 中的 Controllers 文件夹，然后选择：添加 -> 控制器 。

它将显示“添加基架”对话框。如下所示 -

第2步 - 选择 MVC 5控制器 - 空 选项，然后单击“添加” 按钮。添加控制器对话框将出现。如下所示 -

第3步 - 将名称设置为：HomeController，然后单击添加 按钮。

在Controllers文件夹中看到一个新的 C# 文件：HomeController.cs，该文件夹也在Visual Studio中打开并进行编辑。

第4步 - 为了使这个工作的例子，修改控制器类通过使用下面的代码修改Index方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace FirstMVCApp.Controllers
{
    public class HomeController : Controller
    {
        // GET: Default
        public String Index()
        {
            return "Hello World, this is ASP.Net MVC Yiibai";
        }
    }
}

第5步 - 运行该应用程序，将看到浏览器正在显示Index操作方法的结果。如下图所示 -

ASP.Net MVC生命周期

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_life_cycle.html

在本章中，我们将讨论整个MVC管道以及HTTP请求在ASP.NET中通过MVC框架时的生命过程。 在高层次上，生命周期就是用来处理某种类型的请求或改变应用程序状态的一系列步骤或事件。 您可能已经熟悉了各种框架生命周期，这个概念并不是MVC独有的。

例如，ASP.NET webforms平台具有复杂的页面生命周期。 其他.NET平台，如Windows手机应用程序，都有自己的应用程序生命周期。 对于所有这些平台而言，无论采用哪种技术，都是如此，理解处理管道可以帮助您更好了解程序运行的过程，以及利用可用的功能，而MVC也不例外。

MVC有两个生命周期 -

应用程序生命周期

请求生命周期

应用程序生命周期

应用程序生命周期是指应用程序实际开始运行IIS的时间，直到停止。 这由应用程序启动文件中的应用程序开始和结束事件标记。

请求生命周期

每当HTTP请求由我们的应用程序处理时，就是发生的事件序列。

每个MVC应用程序的入口点都以路由开始。在ASP.NET平台收到请求之后，它会找出如何通过URL路由模块来处理它。

模块是可以挂入应用程序生命周期并添加功能的.NET组件。 路由模块负责将传入的URL与我们在应用程序中定义的路由进行匹配。

所有的路由都有一个关联的路由处理器，这是MVC框架的入口点。

MVC框架处理将路由数据转换为可处理请求的具体控制器。控制器创建完成后，下一个主要步骤是“Action Execution”。动作调用者的组件找到并选择适当的Action方法来调用控制器。

在动作结果准备好之后，触发下一个阶段，即执行结果。 MVC分离声明执行结果的结果。 如果结果是视图类型，则视图引擎将被调用，它负责查找和渲染视图。

如果结果不是视图，则操作结果将自行执行。这个结果执行是生成对原始HTTP请求的实际响应。

ASP.Net MVC路由

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_routing.html

路由是指向一个控制器的HTTP请求的过程，这个处理的功能是在System.Web.Routing中实现的。 这个程序集不是ASP.NET MVC的一部分。 它实际上是ASP.NET运行时的一部分，并且作为.NET 3.5 SP1正式发布。

System.Web.Routing由MVC框架使用，但也被ASP.NET动态数据使用。 MVC框架利用路由将请求引导至控制器。 Global.asax文件是应用程序的一部分，可在其中定义应用程序的路由。

这是在上一章创建的MVC应用程序(FirstMVCApp)中的Global.asax中的应用程序启动事件的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace FirstMVCApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}

以下是RouteConfig类的实现，其中包含一个RegisterRoutes方法。

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

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

您将定义路由，这些路由将URL映射到特定的控制器操作。一个动作只是控制器上的一个方法。它也可以从URL中选择参数并将它们作为参数传递给方法。

所以在应用程序中定义的这个路由是默认路由。从上面的代码可以看出，当你看到一个URL以(something)/(something)/(something)的形式到达的时候，第一个是控制器的名称，第二个是动作的名字，第三个是一个ID参数。

理解路由

MVC应用程序使用ASP.NET路由系统，它决定URL如何映射到控制器和操作。

当Visual Studio创建MVC项目时，它会添加一些默认路由来启动。 运行应用程序时，您将看到Visual Studio已将浏览器定向到端口56922。几乎可以肯定地每次在浏览器请求的URL中看到不同的端口号，因为Visual Studio在创建项目时会分配一个随机端口。

在最后一个例子中，我们添加了一个控制器：HomeController ，所以也可以请求任何下面的URL，它们将被定向到HomeController上的Index 操作。如下都是有效的URL -

http://localhost:56922/Home/
-- 或者 --
http://localhost:56922/Home/Index

当浏览器请求 http://mysite/ 或 http://mysite/Home 时，它会从HomeController的Index方法获取输出。

也可以通过更改浏览器中的URL来尝试此操作。在这个例子中，它是http://localhost:56922/，除了端口可能不同。

如果将/Home或/Home/Index追加到URL并按下“Enter”按钮，将看到与MVC应用程序输出相同的结果。

正如在这种情况下可以看到有一个名为HomeController的控制器，这个HomeController将成为MVC应用程序的起点。

Visual Studio为新项目创建的默认路由。但是如果想遵循自己的约定，那么将需要修改路由。

自定义约定

当然可以添加您自己的路由。如果不喜欢这些动作名称，或有不同的ID参数，又或者一般情况下网站的URL结构不同，则可以添加自己的路由项。

下面来看一个简单的例子。考虑有一个包含进程列表的页面，以下是将转到流程页面的代码。

routes.MapRoute(
   "Process",
   "Process/{action}/{id}",
   defaults: new{
      controller = "Process", action = "List ", id = UrlParameter.Optional}
);

当客户端请求进来并寻找具有Process/Action/Id的URL时，它们将会进入到Process控制器。这里可以使动作有点不太一样，默认动作使用的是List而不是Index。

现在到达的请求看起来像localhosts/process。 路由引擎将使用这个路由配置来传递它，所以它将使用List作为的默认行为。

以下是完整的类实现。创建一个名称为：MyRouteConfig 的类，参考代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace FirstMVCApp
{

    public class MyRouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
               "Process", "Process/{action}/{id}",
               defaults: new
               {
                   controller = "Process",
                   action = "List",
                   id =
                  UrlParameter.Optional
               });

            routes.MapRoute(
               name: "Home", url: "{controller}/{action}/{id}",
               defaults: new
               {
                   controller = "Home",
                   action = "Index",
                   id =
                  UrlParameter.Optional
               });
        }
    }
}

第1步 - 运行它并请求一个流程页面，使用以下URL ：http://localhost:63664/Process，应该会看到以下结果 -

将看到一个HTTP 404，因为路由引擎正在查找ProcessController，但是不可用。

第2步 - 通过右键单击解决方案管理器 中的Controllers 文件夹来创建ProcessController，然后选择：添加 -> 控制器 。

它将显示“添加基架” 对话框。如下图所示 -

第3步 - 选择MVC 5控制器 - 空选项，然后单击“添加” 按钮。添加控制器对话框将出现。

第4步 - 将名称设置为ProcessController，然后单击“添加” 按钮。如下图所示 -

现在，将在Controllers文件夹中看到一个新的 C# 文件：ProcessController.cs，在Visual Studio中打开并进行编辑。如下图所示 -

第5步 - 将返回类型从ActionResult更改为String，并使用以下代码从此操作方法返回一些字符串。修改后的 ProcessController 文件中的代码如下 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace FirstMVCApp.Controllers
{
    public class ProcessController : Controller
    {
        // GET: Process
        public String Index()
        {
            return "ProcessController.Index() Page.";
        }

        // GET: Process
        public String List()
        {
            return "ProcessController.List() Page.";
        }
    }
}

第6步 - 当运行这个应用程序，会看到默认路由的结果。当指定以下URL( http://localhost:56922/Process/List )时，您将看到ProcessController的结果。

ASP.Net MVC控制器

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_controllers.html

控制器本质上是ASP.NET MVC应用程序的中心单元。 它是第一个请求接收者，与传入的HTTP请求进行交互。 因此，控制器决定选择哪个模型，然后从模型中获取数据，并将其传递给相应的视图，然后渲染视图。 实际上，控制器正在控制应用程序的整体流程，并输出正确的输出。

控制器是继承自System.Web.Mvc.Controller的 C# 类，它是内置的控制器基类。 控制器中的每个公共方法都称为一个操作方法，这意味着您可以通过某个URL从Web调用它来执行操作。

MVC约定是将控制器放置在Visual Studio 在项目设置时创建的文件夹(Controllers)中。

我们来看一个简单的控制器例子，通过创建一个新的ASP.Net MVC项目。

第1步 - 打开Visual Studio，然后单击菜单：文件 -> 新建 -> 项目 选项。一个新的项目对话框打开。
第2步 - 在左侧窗格中，选择模板：Visual C# -> Web 。
第3步 - 在中间窗格中，选择“ASP.NET Web应用程序” 。如下图所示 -

第4步 - 在名称字段中输入项目名称：MVCControllerDemo，然后单击确定 继续。 您将看到以下对话框，要求为ASP.NET项目设置初始内容。

第5步 - 为了简单起见，选择空 选项，并在“为以下项添加文件夹和核心引用”部分选中MVC复选框，然后单击【确定】。

它将使用最少的预定义内容创建一个基本的MVC项目。

当Visual Studio项目创建以后，在“解决方案资源管理器” 窗口中看到许多文件和文件夹。

由于这里从一个空的项目模板中创建了ASP.Net MVC项目，因此目前应用程序不包含任何要运行的东西。

第6步 - 通过在解决方案资源管理器 中右键单击 Controllers 文件夹来添加一个：EmployeeController。选择：添加 -> 控制器 。如下所示 -

它将显示“添加基架” 对话框。如下所示 -

第7步 - 选择MVC 5控制器 - 空 选项，然后单击“添加” 按钮。添加控制器对话框将出现。
第8步 - 将名称设置为：EmployeeController ，然后单击“添加” 按钮。如下所示 -

在Controllers 文件夹中看到一个新的 C# 文件 - EmployeeController.cs，在Visual Studio中打开并进行编辑。默认代码如下所示 -

自定义路由规则

现在，在这个应用程序中，我们将使用默认路由为EmployeeController控制器，这需要添加一个自定义的路由。请参考以下步骤实现 -

第1步 - 转到“App_Start”文件夹下的 RouteConfig.cs 文件，并添加以下路由。

routes.MapRoute(
   "Employee", "Employee/{name}", new{
      controller = "Employee", action = "Search", name =
      UrlParameter.Optional });

以下是RouteConfig.cs 文件的完整实现。参考以下代码 -

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace MVCControllerDemo {
   public class RouteConfig {
      public static void RegisterRoutes(RouteCollection routes){
         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
         routes.MapRoute(
            "Employee", "Employee/{name}", new{
               controller = "Employee", action = "Search", name = UrlParameter.Optional });

         routes.MapRoute(
            name: "Default", url: "{controller}/{action}/{id}", defaults: new{
               controller = "Home", action = "Index", id = UrlParameter.Optional });
      }
   }
}

考虑一个场景，其中任何用户来到并搜索雇员，指定URL为Employee/Maxsu。 在这种情况下，Maxsu将被视为参数名称，而不是操作的方法。所以在这种情况下，默认路由将不会工作显示。

当参数传递时，要从浏览器获取传入值，MVC框架提供了一个简单的方法来解决这个问题。它是通过使用操作方法内部的参数。

第2步 - 使用以下代码更改EmployeeController类。参考以下代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers
{
    public class EmployeeController : Controller
    {
        // GET: Employee
        public ActionResult Search(string name)
        {
            var input = Server.HtmlEncode(name);
            return Content(input);
        }
    }
}

如果将一个参数添加到操作方法中，则MVC框架将查找与参数名称匹配的值。它将应用所有可能的组合来找出参数值。它将搜索路由数据，查询字符串等

因此，如果请求：/Employee/Maxsu，那么MVC框架将决定需要一个带有UserInput的参数，然后Maxsu会从URL中挑选出来并自动传递。

Server.HtmlEncode将简单地转换任何形式的纯文本恶意脚本。当上面的代码被编译并执行并请求以下URL：http://localhost:51066/Employee/Maxsu时，将得到以下输出。参考下图所示 -

正如你在上面的屏幕截图中看到的，Maxsu是从URL中选取的。

ASP.Net MVC动作

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_actions.html

ASP.NET MVC操作方法负责执行请求并生成响应。默认情况下，它以ActionResult的形式生成响应。操作通常具有与用户交互的一对一映射。

例如，在浏览器中输入一个URL，点击任何特定的链接，提交表单等。这些用户交互中的每一个都会导致请求被发送到服务器。 在每种情况下，请求的URL都包含MVC框架用来调用操作方法的信息。动作方法的一个限制是它们必须是实例方法，所以它们不能是静态方法。也没有返回值的限制。 所以可以返回字符串，整数等。

请求处理

动作是MVC应用程序中的最终请求目标，它使用控制器基类。我们来看看请求处理。

当URL到达时，如/Home/index，这是一个UrlRoutingModule，它检查并理解路由表中配置的内容并知道如何处理该URL。

UrlRoutingModule将在路由表中配置的信息放在一起，并将控制交给MVC路由处理程序。

MVC路由处理程序将控制器传递给作为HTTP处理程序的MvcHandler。

MvcHandler使用一个控制器工厂实例化控制器，它知道实例化哪个控制器，因为它在该控制器值的RouteData中查找。

当MvcHandler有一个控制器，MvcHandler唯一知道的就是IController接口，所以它只是告诉控制器执行。

当它告诉控制器执行时，这是从MVC的控制器基类派生的。 Execute方法创建一个动作调用者，并告诉动作调用者去找到一个方法来调用，找到一个调用的动作。

动作调用者再次在RouteData中查找并找到从路由引擎传递的动作参数。

动作类型

动作基本上返回不同类型的动作结果。ActionResult类是所有操作结果的基础。 以下是不同种类的行为结果及其行为的列表。

编号 | 名称 | 行为
1 | ContentResult | 返回一个字符串
2 | FileContentResult | 返回文件内容
3 | FilePathResult | 返回文件内容
4 | FileStreamResult | 返回文件内容
5 | EmptyResult | 什么都不返回
6 | JavaScriptResult | 返回脚本执行
7 | JsonResult | 返回JSON格式的数据
8 | RedirectToResult | 重定向到指定的网址
9 | HttpUnauthorizedResult | 返回403 HTTP状态码
10 | RedirectToRouteResult | 重定向到不同的动作/不同的控制器动作
11 | ViewResult | 作为视图引擎的响应收到
12 | PartialViewResult | 作为视图引擎的响应收到

我们来看看前一章(ASP.Net MVC控件器的MVCControllerDemo项目)中创建EmployeeController的一个简单示例。参考以下示例代码 -

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers {
   public class EmployeeController : Controller{
      // GET: Employee
      public ActionResult Search(string name){
         var input = Server.HtmlEncode(name);
         return Content(input);
      }
   }
}

当请求以下URL：http://localhost:51066/Employee/Maxsu 时，将收到操作的输出如下 -

添加控制器

现在，在项目上再添加另一个控制器。

第1步 - 通过在解决方案资源管理器 中右键单击 Controllers 文件夹来添加一个：控制器：CustomerController。选择：添加 -> 控制器 。如下所示 -

它将显示“添加基架” 对话框。如下所示 -

第2步 - 选择MVC 5控制器 - 空 选项，然后单击“添加” 按钮。添加控制器对话框将出现。
第3步 - 将名称设置为：CustomerController ，然后单击“添加” 按钮。

现在，已经在Controllers文件夹中看到一个新的 C# 文件“CustomerController.cs” ，在Visual Studio中打开并进行编辑，如下所示 -

同样，再添加一个名为HomeController的控制器。 以下是HomeController.cs类的实现。参考代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public string Index()
        {
            return "This is Home MVCControllerDemo.HomeController.Index() Action.";
        }
    }
}

第4步 - 运行此应用程序，将收到以下输出 -

第5步 - 在上面创建的CustomerController.cs类中添加一个方法，如以下代码 -

public string GetAllCustomers(){
   return @"<ul>
      <li>Max Su</li>
      <li>Mark Jasper</li>
      <li>Allan Java</li>
      <li>Ruby Jerry</li>
   </ul>";
}

第6步 - 运行该应用程序并请求URL: http://localhost:51066/Customer/GetAllCustomers ，将看到以下输出 -

也可以重定向到同一控制器的操作，甚至是不同的控制器。以下是一个简单的例子，将通过使用以下代码更改HomeController中的代码，从HomeController重定向到CustomerController。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCControllerDemo.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return RedirectToAction("GetAllCustomers", "Customer");
        }
    }
}

上面代码中，使用了RedirectToAction()方法，它有两个参数：操作名称和控制器名称。

当运行这个应用程序时，会看到默认路由将重定向到/Customer/GetAllCustomers

ASP.Net MVC过滤器

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_filters.html

在ASP.NET MVC中，控制器定义的操作方法通常与可能的用户交互具有一对一的关系，但有时候您希望在调用操作方法之前或者在操作方法运行之后执行逻辑。

为了支持这个功能，ASP.NET MVC提供了过滤器。 过滤器是一个自定义类，它提供了一种声明式和程序式的方法，可将操作前和操作后行为添加到控制器操作方法中。

动作过滤器

动作过滤器是一个属性，可以将其应用于控制器动作或整个控制器，以修改执行动作的方式。 ASP.NET MVC框架包含几个操作过滤器 -

OutputCache - 将控制器操作的输出缓存指定的时间量。

HandleError - 处理执行控制器操作时引发的错误。

Authorize - 使能够限制对特定用户或角色的访问。

过滤器的类型

ASP.NET MVC框架支持四种不同类型的过滤器 -

授权过滤器 - 实现IAuthorizationFilter属性。

动作过滤器 - 实现IActionFilter属性。

结果过滤器 - 实现IResultFilter属性。

异常过滤器 - 实现IExceptionFilter属性。

过滤器按上面列出的顺序执行。例如，授权过滤器始终在每个其他类型的过滤器之后执行操作过滤器和异常过滤器之前执行。

授权过滤器 用于实现控制器操作的身份验证和授权。 例如，授权过滤器是授权过滤器的一个例子。

下面来看一个简单的例子，创建一个新的ASP.Net MVC项目。打开Visual Studio，然后单击菜单：文件 -> 新建 -> 项目 选项。创建一个名称为：MVCFiltersDemo 的MVC项目。

详细创建过程请参考：http://www.yiibai.com/asp.net_mvc/asp.net_mvc_getting_started.html

通过在解决方案资源管理器 中右键单击 Controllers 文件夹来添加一个控件器：HomeController。在弹出菜单项中选择：添加 -> 控制器 。

应用操作筛选器

动作过滤器可以应用于单独的控制器动作或整个控制器。 例如，操作筛选器OutputCache应用于名为Index()的操作，该操作返回字符串。 此过滤器会将该操作返回的值缓存15秒。

为了使这个实现这个例子，让我们修改控制器类通过改变称为Index操作方法使用下面的代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCFiltersDemo.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        // GET: Home
        [OutputCache(Duration = 15)]
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }
    }
}

运行此应用程序时，将看到浏览器正在显示Index操作方法的结果，如下所示 -

再添加另一个操作方法，它用于显示当前时间 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCFiltersDemo.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        [OutputCache(Duration = 15)]
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }
        // 返回当前时间
        [OutputCache(Duration = 20)]
        public string GetCurrentTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

请求以下URL：http://localhost:54713/Home/GetCurrentTime，将收到以下输出 -

如果刷新浏览器，则会看到相同的时间，因为该操作被缓存了20秒。 20秒后刷新它将被更新。

自定义过滤器

要创建自己的自定义过滤器，ASP.NET MVC框架提供了一个叫作ActionFilterAttribute的基类。 这个类实现了IActionFilter和IResultFilter接口，都是从Filter类派生的。

下面来看看看自定义过滤器的一个简单的例子，通过在项目中创建一个新的文件夹：ActionFilters 。添加一个类，右键单击ActionFilters 文件夹并选择：添加 -> 类 。

在类名称字段中输入MyLogActionFilter，然后单击“添加” 按钮。

这个类将从ActionFilterAttribute派生，它是一个基类，并覆盖下面的方法。 以下是MyLogActionFilter的完整实现。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace MVCFiltersDemo.ActionFilters
{
    public class MyLogActionFilter: ActionFilterAttribute
    {

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            Log("##########OnActionExecuted", filterContext.RouteData);
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            Log("##########OnResultExecuting", filterContext.RouteData);
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            Log("##########OnResultExecuted", filterContext.RouteData);
        }

        private void Log(string methodName, RouteData routeData)
        {
            var controllerName = routeData.Values["controller"];
            var actionName = routeData.Values["action"];

            var message = String.Format(
               "{0} controller:{1} action:{2}", methodName, controllerName, actionName);
            System.Console.WriteLine("############################# YES ##################");
            Debug.WriteLine(message, "Action Filter Log");
        }
    }

}

现在，使用以下代码将日志过滤器应用于HomeController控制器。

using MVCFiltersDemo.ActionFilters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCFiltersDemo.Controllers
{
    [MyLogActionFilter]
    public class HomeController : Controller
    {
        // GET: Home
        [OutputCache(Duration = 10)]
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }

        // 返回当前时间
        [OutputCache(Duration = 10)]
        public string GetCurrentTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

打开项目中的App_Start，打开文件FilterConfig.cs ，添加以下代码注册过滤器：MyLogActionFilter -

using MVCFiltersDemo.ActionFilters;
using System.Web;
using System.Web.Mvc;

namespace MVCFiltersDemo
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            // 注册自定义Action过滤器：优先级最低，但是可以作用到所有的控制器和Action
            filters.Add(new MyLogActionFilter());
        }
    }
}

运行应用程序，然后观察输出窗口。应该会看到类似(输出的最后几行)的结果 -

如上图所示，处理动作的每个阶段都有被记录到Visual Studio输出窗口中了。

ASP.Net MVC选择器

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_selectors.html

动作选择器是可以应用于动作方法的属性，用于影响响应请求时调用哪个动作方法。 它有助于路由引擎选择正确的操作方法来处理特定的请求。

当你编写动作方法时，它扮演着非常关键的角色。 这些选择器将根据动作方法前面给出的修改后的名称来决定方法调用的行为。动作选择器通常用于别名操作方法的名称。

动作选择器有三种类型的属性 -

ActionName

NonAction

ActionVerbs

1. ActionName

这个类表示一个用于动作名称的属性。它还允许开发人员使用与方法名称不同的动作名称(即动作的别名)。

我们来创建一个项目：MVCSelectors，在这个项目中，创建一个控制器 - HomeController ，它包含有两个操作方法。参考以下代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }

        public string GetCurrentTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

通过在GetCurrentTime()方法之上写[ActionName("CurrentTime")]来应用GetCurrentTime的ActionName选择器，如以下代码所示 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }

        [ActionName("CurrentTime")]
        public string GetCurrentTime()
        {
            return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

现在运行该应用程序并在浏览器URL栏中输入以下URL：http://localhost:62833/Home/CurrentTime(不是GetCurrentTime)，将收到以下输出结果 -

可以看到已经使用了CurrentTime，而不是原来的动作名称，在上面正常的URL应该是:GetCurrentTime。

2. NonAction

NonAction是另一个内置属性，它表示Controller的公共方法不是一个操作方法。当想要一个方法不要被当作一个操作方法来处理的时候，就可以使用它了。

下面来看看一个简单的例子，在HomeController中添加另一个方法，并使用下面的代码应用NonAction属性。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public string Index()
        {
            return "This is ASP.Net MVC Filters Tutorial";
        }

        [ActionName("CurrentTime")]
        public string GetCurrentTime()
        {
            return TimeString();
        }

        [NonAction]
        public string TimeString()
        {
            return "当前时间是： " + DateTime.Now.ToString("T");
        }
    }
}

新的方法TimeString()是要被GetCurrentTime()方法调用的，但不能在URL中作为动作使用。

运行这个应用程序，并在浏览器中指定以下URL： http://localhost:62466/Home/CurrentTime ， 将收到以下输出。如下图所示 -

现在来看看在URL中使用/Home/TimeString作为动作，看看会发生什么。如下图所示 -

可以看到它给出了“404-找不到” 的错误。

3. ActionVerbs

另一个可以应用的选择器过滤器是ActionVerbs属性。所以这限制了特定行为对特定的HttpVerbs的指示。可以定义两个具有相同名称的不同操作方法，但是一个操作方法会响应HTTP Get请求，另一个操作方法会响应HTTP Post请求。

MVC框架支持以下ActionVerbs -

HttpGet

HttpPost

HttpPut

HttpDelete

HttpOptions

HttpPatch

下面来看一个简单的例子，创建一个控制器 - EmployeeController。

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class EmployeeController : Controller
    {
        // GET: Employee
        public ActionResult Search(string name = "No name Entered")
        {
            var input = Server.HtmlEncode(name);
            return Content(input);
        }
    }
}

现在使用下面的代码添加另一个具有相同名称的操作方法。

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class EmployeeController : Controller
    {
        public ActionResult Search(string name)
        {
            var input = Server.HtmlEncode(name);
            return Content(input);
        }

        public ActionResult Search()
        {
            var input = "Another Search action";
            return Content(input);
        }
    }
}

当运行这个应用程序时，它会给出一个错误，因为MVC框架无法确定应该为请求选择哪个操作方法。

使用下面的代码来指定HttpGet ActionVerb作为响应的动作。

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCSelectors.Controllers
{
    public class EmployeeController : Controller
    {
        public ActionResult Search(string name)
        {
            var input = Server.HtmlEncode(name);
            return Content(input);
        }
        [HttpGet]
        public ActionResult Search()
        {
            var input = "Another Search action";
            return Content(input);
        }
    }
}

当运行此应用程序时，将收到以下输出结果 -

ASP.Net MVC视图

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_views.html

在ASP.NET MVC应用程序中，当在URL中指定路径时，可像页面那样但不包含对应的任何内容直接显示给用户。 在ASP.NET MVC应用程序中最接近页面的东西就是视图。

在ASP.NET MVC应用程序中，所有传入的浏览器请求都由控制器处理，并将这些请求映射到控制器操作。 控制器操作可能会返回一个视图，或者也可能执行一些其他类型的操作，例如重定向到另一个控制器动作。

下面通过创建一个新的ASP.NET MVC项目，来演示如何应用视图的简单例子。
打开Visual Studio，然后单击菜单：文件 -> 新建 -> 项目 选项。创建一个名称为：MVCViewDemo 的MVC项目。

详细创建过程请参考：http://www.yiibai.com/asp.net_mvc/asp.net_mvc_getting_started.html

通过在解决方案资源管理器 中右键单击 Controllers 文件夹来添加一个控件器：HomeController。在弹出菜单项中选择：添加 -> 控制器 。

将在Controllers文件夹中看到一个新的 C# 文件 - HomeController.cs，x在Visual Studio中打开并进行编辑。修改更新HomeController.cs文件中的代码，其中包含两个操作方法，如下面的代码所示。

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCViewDemo.Controllers {
   public class HomeController : Controller{
      // GET: Home
      public ActionResult Index(){
         return View();
      }

      public string Mycontroller(){
         return "Hi, I am a controller";
      }
   }
}

运行这个应用程序，打开浏览器访问：http://localhost:63461/Home/MyController，将收到以下输出结果 -

由于MyController操作只是返回字符串，要从操作返回一个View，需要首先添加一个View。参考以下步骤 -

第1步： 在添加视图之前，添加另一个操作，它将返回一个默认视图。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCViewDemo.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }

        public string Mycontroller()
        {
            return "Hi, I am a controller";
        }

        public ActionResult MyView()
        {
            return View();
        }
    }
}

运行这个应用程序，并在浏览器的URL中添加http://localhost:63461/Home/MyView，然后按回车，将收到以下输出。

在这里看到有一个错误，这个错误实际上是相当具有描述性的，告诉我们它找不到MyView视图。

第2步 - 要添加一个视图，右键单击MyView动作(方法)并选择添加视图。如下图所示 -

它将显示“添加视图” 对话框，并将添加默认名称。

第3步 - 取消选中“使用布局页面”复选框，然后单击“添加”按钮。现在新建的这个视图内有默认的代码。如下所示 -

第4步 - 使用以下代码在此视图中添加一些文本。

@{
   Layout = null;
}

<!DOCTYPE html>
<html>
   <head>
      <meta name = "viewport" content = "width = device-width" />
      <title>MyView</title>
   </head>

   <body>
      <div>
         Hi, I am a view
      </div>
   </body>

</html>

第5步 - 运行该应用程序，并将打开浏览器访问URL：http://localhost:63461/Home/MyView， 将收到以下输出。

ASP.Net MVC数据模型

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_data_model.html

在本章中，我们将讨论和学习在ASP.NET MVC Framework应用程序中构建模型。模型存储数据，并根据来自控制器中的命令检索出来，最终在视图中显示这些数据。

Model是一个类的集合，应用程序中可使用这些数据来编写业务逻辑。 因此，基本上模型是业务领域特定的容器。它用于与数据库进行交互。也可以用来操纵数据来实现业务逻辑。

下面通过创建一个新的ASP.NET MVC项目，来演示如何应用模型的简单例子。
打开Visual Studio，然后单击菜单：文件 -> 新建 -> 项目 选项。创建一个名称为：MVCSimpleApp 的MVC项目。

详细创建过程请参考：http://www.yiibai.com/asp.net_mvc/asp.net_mvc_getting_started.html

通过在解决方案资源管理器 中右键单击 Controllers 文件夹来添加一个控件器：HomeController。在弹出菜单项中选择：添加 -> 控制器 。
选择包含读/写操作的MVC 5控制器 选项，这个模板将为控制器创建一个具有默认操作的Index方法。这也将列出其他方法，如：Edit/Delete/Create 。

第1步 - 创建控制器

在Controllers文件夹中看到一个新的 C# 文件 - EmployeeController.cs，在Visual Studio中打开并进行编辑。修改更新EmployeeController.cs文件中的代码，其中包含一些默认的动作方法，如下面的代码所示 -

using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCSimpleApp.Controllers {
   public class EmployeeController : Controller{
      // GET: Employee
      public ActionResult Index(){
         return View();
      }

      // GET: Employee/Details/5
      public ActionResult Details(int id){
         return View();
      }

      // GET: Employee/Create
      public ActionResult Create(){
         return View();
      }

      // POST: Employee/Create
      [HttpPost]
      public ActionResult Create(FormCollection collection){
         try{
            // TODO: Add insert logic here
            return RedirectToAction("Index");
         }catch{
            return View();
         }
      }

      // GET: Employee/Edit/5
      public ActionResult Edit(int id){
         return View();
      }

      // POST: Employee/Edit/5
      [HttpPost]
      public ActionResult Edit(int id, FormCollection collection){
         try{
            // TODO: Add update logic here
            return RedirectToAction("Index");
         }catch{
            return View();
         }
      }

      // GET: Employee/Delete/5
      public ActionResult Delete(int id){
         return View();
      }

      // POST: Employee/Delete/5
      [HttpPost]
      public ActionResult Delete(int id, FormCollection collection){
         try{
            // TODO: Add delete logic here
            return RedirectToAction("Index");
         }catch{
            return View();
         }
      }
   }
}

第2步 - 添加模型

右键单击解决方案资源管理器 中的Models 文件夹，然后在弹出菜单项中选择：添加 -> 类 ， 将看到添加新项目对话框，填写模型名称为：Employee.cs ，如下图所示 -

第3步 - 使用下面的代码将一些属性添加到Employee类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MVCSimpleApp.Models {
   public class Employee{
      public int ID { get; set; }
      public string Name { get; set; }
      public DateTime JoiningDate { get; set; }
      public int Age { get; set; }
   }
}

通过添加一个方法来更新EmployeeController.cs文件，该方法将返回员工列表。

[NonAction]
public List<Employee> GetEmployeeList(){
   return new List<Employee>{
      new Employee{
         ID = 1,
         Name = "Allan",
         JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
         Age = 23
      },

      new Employee{
         ID = 2,
         Name = "Carson",
         JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
         Age = 45
      },

      new Employee{
         ID = 3,
         Name = "Carson",
         JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
         Age = 37
      },

      new Employee{
         ID = 4,
         Name = "Laura",
         JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
         Age = 26
      },
   };
}

第4步 - 更新索引操作方法，如下面的代码所示。

        // GET: Employee
        public ActionResult Index()
        {
            var employees = from e in GetEmployeeList()
                            orderby e.ID
                            select e;
            return View(employees);
        }

第5步 - 运行这个应用程序，打开浏览器访问URL：http://localhost:64466/employee，将看到以下输出。

正如在上面的截图所看到的，有一个错误，这个错误实际上是相当具有描述性的，告诉我们它找不到索引视图。

第6步 - 因此，要添加一个视图，右键单击Index动作方法，并选择添加视图。它将显示“添加视图”对话框，并将添加默认名称。参考下图 -

第7步 - 从模板下拉列表中选择列表，在模型类下拉列表中选择Employee，并取消选中“使用布局页面”复选框，然后单击“添加” 按钮。

它会在这个视图中自动添加一些默认的代码。如下所示 -

@model IEnumerable<MVCSimpleApp.Models.Employee>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.JoiningDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Age)
            </th>
            <th></th>
        </tr>

    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.JoiningDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Age)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
                @Html.ActionLink("Details", "Details", new { id=item.ID }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.ID })
            </td>
        </tr>
    }

    </table>
</body>
</html>

第8步 - 运行这个应用程序，将看到以下输出。

ASP.Net MVC助手

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_helpers.html

在ASP.Net Web表单中，开发人员正在使用工具箱来在任何特定的页面上添加控件。 但是，在ASP.NET MVC应用程序中，没有工具箱可用于在视图上拖放HTML控件。 在ASP.NET MVC应用程序中，如果想创建一个视图，它应该包含HTML代码。 所以那些刚接触MVC的开发者，特别是在网页表单的背景下，发现这个有点难。

为了克服这个问题，ASP.NET MVC提供了HtmlHelper类，它包含不同的方法来帮助你编程创建HTML控件。 所有的HtmlHelper方法都会生成HTML并以字符串形式返回结果。 最终的HTML是由这些函数在运行时生成的。 HtmlHelper类用于生成UI，不应该在控制器或模型中使用。

有不同类型的帮手方法。

Createinputs - 为文本框和按钮创建输入。

Createlinks - 创建基于来自路由表的信息的链接。

Createforms - 创建表单标签，可以回发到动作，或回发到另一个控制器上的操作。

如果有看过前面视图教程文章中(项目：MVCSimpleApp) EmployeeController控制器的Index动作生成的视图，将看到以Html开始的操作符前缀，如Html.ActionLink和Html.DisplayNameFor等，如下面的代码所示。

@model IEnumerable<MVCSimpleApp.Models.Employee>
@{
   Layout = null;
}

<!DOCTYPE html>
<html>
   <head>
      <meta name = "viewport" content = "width = device-width" />
      <title>Index</title>
   </head>

   <body>
      <p>
         @Html.ActionLink("Create New", "Create")
      </p>

      <table class = "table">
         <tr>
            <th>
               @Html.DisplayNameFor(model => model.Name)
            </th>

            <th>
               @Html.DisplayNameFor(model => model.JoiningDate)
            </th>

            <th>
               @Html.DisplayNameFor(model => model.Age)
            </th>

            <th></th>
         </tr>

         @foreach (var item in Model) {
            <tr>
               <td>
                  @Html.DisplayFor(modelItem => item.Name)
               </td>

               <td>
                  @Html.DisplayFor(modelItem => item.JoiningDate)
               </td>

               <td>
                  @Html.DisplayFor(modelItem => item.Age)
               </td>

               <td>
                  @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
                  @Html.ActionLink("Details", "Details", new { id = item.ID }) |
                  @Html.ActionLink("Delete", "Delete", new { id = item.ID })
               </td>
            </tr>
         }

      </table>
   </body>
</html>

这个HTML是从ViewPage基类继承的一个属性。所以，它在所有的视图中都可用，并返回一个名为HTMLHelper的实例。

我们来看看一个简单的例子，让用户可以编辑员工信息。 因此，此编辑操作将使用大量不同的HTML助手。

如果看上面的代码，会在最后部分看到下面的HTML Helper方法 -

@Html.ActionLink("Edit", "Edit", new { id = item.ID })

在ActionLink助手中，第一个参数是“Edit”链接，第二个参数是控制器中的动作方法，也是“Edit”，第三个参数ID是要编辑的员工编号。

我们通过添加静态列表来更改EmployeeController类，并使用以下代码更改索引操作。

public static List<Employee> empList = new List<Employee>{
   new Employee{
      ID = 1,
      Name = "Allan",
      JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
      Age = 23
   },

   new Employee{
      ID = 2,
      Name = "Carson",
      JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
      Age = 45
   },

   new Employee{
      ID = 3,
      Name = "Carson",
      JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
      Age = 37
   },

   new Employee{
      ID = 4,
      Name = "Laura",
      JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
      Age = 26
   },

};

public ActionResult Index(){
   var employees = from e in empList
   orderby e.ID
   select e;
   return View(employees);
}

下面来更新编辑操作。您将看到两个编辑操作，一个用于GET，一个用于POST。这里更新GET的编辑操作，其中只有参数中的Id，如下面的代码所示。

// GET: Employee/Edit/5
public ActionResult Edit(int id){
   List<Employee> empList = GetEmployeeList();
   var employee = empList.Single(m => m.ID == id);
   return View(employee);
}

现在，我们知道有编辑的动作，但是没有任何对应此动作的视图。 所以还需要添加一个视图(View)。 为此，请右键单击Edit操作，然后选择添加视图。从“模板”下拉列表中选择Edit，从“模型”下拉列表中选择“Employee” -

以下是Edit视图中的默认实现。参考以下示例代码 -

@model MVCSimpleApp.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Edit</title>
</head>
<body>
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>Employee</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            @Html.HiddenFor(model => model.ID)

            <div class="form-group">
                @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.JoiningDate, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.JoiningDate, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.JoiningDate, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.Age, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Age, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Age, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Save" class="btn btn-default" />
                </div>
            </div>
        </div>
    }

    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</body>
</html>

Html.BeginForm非常有用，因为它使您能够更改URL，更改方法等。

在上面的代码中，看到另一个HTML助手:@ HTML.HiddenFor，它用于生成隐藏的字段。

MVC框架足够聪明地发现这个ID字段在模型类中，因此它需要防止被编辑编修改，这就是为什么它被标记为隐藏字段的原因。

Html.LabelFor HTML助手在屏幕上创建标签。如果在进行更改时错误地输入了任何内容，则Html.ValidationMessageFor助手将显示正确的错误消息。

还需要更改POST的编辑操作，需要更新员工信息时，它就会调用这个动作。参考以下代码 -

        // POST: Employee/Edit/5
        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            try
            {
                // TODO: Add update logic here
                List<Employee> empList = GetEmployeeList();
                var employee = empList.Single(m => m.ID == id);
                if (TryUpdateModel(employee))
                {
                    //To Do:- database code
                    return RedirectToAction("Index");
                }
                return View(employee);
            }
            catch
            {
                return View();
            }
        }

让我们运行这个应用程序，并请求以下URL：http://localhost:64466/Employee员工。将会看到以下输出。

点击任何特定员工的编辑链接，以点击员工编号为1编辑链接为示例，您将看到以下视图显示结果。

将年龄从23岁改为29岁，然后点击“保存”按钮，然后会在Index视图中看到更新的年龄。

ASP.Net MVC模型绑定

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_model_binding.html

ASP.NET MVC模型绑定允许您将HTTP请求数据与模型进行映射。 这是使用HTTP请求中的浏览器发送的数据创建.NET对象的过程。对于ASP.Net MVC而言，ASP.NET Web Forms开发人员大多都感到困惑，因为视图的值在到达控制类的动作方法时会如何转换为Model类，这个转换由模型绑定器完成。

模型绑定是HTTP请求和 C# 操作方法之间的良好设计桥梁。 它使开发人员可以方便地使用表单(视图)上的数据，因为POST和GET会自动传输到指定的数据模型中。 ASP.NET MVC使用默认的绑定器来完成这个场景。

我们来看一个简单的例子，在上一章的项目中添加一个“创建视图”，我们将看到如何从视图中获取这些值传到EmployeeController动作方法。

以下是POST的创建动作方法。

// POST: Employee/Create
[HttpPost]
public ActionResult Create(FormCollection collection){
   try{
      // TODO: Add insert logic here
      return RedirectToAction("Index");
   }catch{
      return View();
   }
}

右键单击Create动作方法，然后选择添加视图…， 它将显示添加视图对话框。
如在上面的屏幕截图中看到的那样，默认的动作名字已经在输入框中了。现在从“模板”下拉列表中选择“Create”，从“模型”下拉列表中选择“Employee”。参考下图 -

创建的Create.cshtml 视图中看到默认代码如下 -

@model MVCSimpleApp.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Create</title>
</head>
<body>
    @using (Html.BeginForm())
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>Employee</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.JoiningDate, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.JoiningDate, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.JoiningDate, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.Age, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.Age, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.Age, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }

    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</body>
</html>

当用户在创建视图上输入值时，它在FormCollection和Request.Form中都可用。可以使用这些值中的任何一个来填充视图中的员工信息。

使用下面的代码来(FormCollection)创建员工信息。参考以下代码 -

// POST: Employee/Create
[HttpPost]
public ActionResult Create(FormCollection collection){
   try {
      Employee emp = new Employee();
      emp.Name = collection["Name"];
      DateTime jDate;
      DateTime.TryParse(collection["DOB"], out jDate);
      emp.JoiningDate = jDate;
      string age = collection["Age"];
      emp.Age = Int32.Parse(age);
      empList.Add(emp);
      return RedirectToAction("Index");
   }catch {
      return View();
   }
}

运行这个应用程序并请问这个URL： http：// localhost：63004 / Employee /。将收到以下输出结果 -

点击页面顶部的“Create New”链接，它将转到下面的视图。如下所示 -

输入想要添加的其他员工的数据。如下所示 -

点击创建按钮，会看到新员工被添加到列表中。如下图所示 -

在上面的示例中，从HTML视图中获取所有发布的值，然后将这些值映射到Employee属性并逐一分配它们。

在这种情况下，我们也将在发布的值与Model属性的格式不相同的情况下进行类型转换。

这也被称为手动绑定，这种类型的实现对于简单和小数据模型可能不是那么糟糕。 但是，如果对于具有大量的数据类型的模型，则需要大量的类型转换，那么可以利用ASP.NET MVC Model绑定的强大功能和易用性。

下面来看看为模型绑定所做的另一个例子。

我们需要改变Create方法的参数来接受Employee模型对象而不是FormCollection对象，如下面的代码所示。

// POST: Employee/Create
[HttpPost]
public ActionResult Create(Employee emp){
   try{
      empList.Add(emp);
      return RedirectToAction("Index");
   }catch{
      return View();
   }
}

现在，模型绑定的魔力取决于提供值的HTML变量的id。

对于Employee模型，HTML输入字段的id应该与Employee模型的属性名称相同，可以看到Visual Studio在创建视图时使用了相同的模型属性名称。如下代码 -

@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })

映射将基于默认的属性名称。在这里，您会发现HTML助手方法非常有用，因为这些助手方法将生成HTML，它将具有适当的名称以使模型绑定起作用。

运行修改代码的应用程序，与上面的运行结果一样。

ASP.Net MVC与SQL Server数据库操作

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_databases.html

在本教程之前，创建的所有ASP.NET MVC应用程序中，我们一直在将来自控制器的硬编码数据传递给视图模板。 但是，构建真正的Web应用程序，可能需要使用真实的数据库。 在本章中，我们将学习如何使用数据库引擎来存储和检索应用程序所需的数据。

要存储和检索数据，我们将使用Entity框架的.NET Framework数据访问技术来定义和使用模型。

Entity框架(EF)支持Code First技术，该技术允许通过编写简单的类来创建模型对象，然后从类中随时创建数据库，从而实现非常干净和快速的开发流程。

下面来看一个简单的例子，我们将在这个例子中添加Entity框架的支持来访问数据库。

第1步 - 创建一个项目：MVCDatabaseAccess，如下所示 -

要安装Entity Framework框架，右键单击项目，然后选择管理NuGet程序包 ，在弹出的界面中搜索：Entity framework，如下图所示 -

选择Entity framework，然后点击“Install”按钮。它将打开预览对话框 -

接受安装协议，如下图所示 -

当Entity framework框架安装成功，会看到下面的截图中所示的绿色“勾”的图标。如下图所示 -

添加DBContext

我们需要向Employee模型添加另一个类，该类将与Entity Framework进行通信，以使用以下代码检索和保存数据。

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

using System.Web;
namespace MVCDatabaseAccess.Models
{
    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime JoiningDate { get; set; }
        public int Age { get; set; }
    }

    public class EmpDBContext : DbContext
    {
        public EmpDBContext()
        { }
        public DbSet<Employee> Employees { get; set; }
    }
}

如上所示，EmpDBContext是从一个名为DbContext的EF类派生的。在这个类中有一个名为DbSet的属性它基本上表示想查询和保存的实体。

连接字符串

我们需要在Web.config文件中的<configuration>标记下为数据库指定连接字符串。

 <connectionStrings>
    <add name = "EmpDBContext" connectionString = "Data Source=MY-PC;Initial Catalog=testdb;Integrated Security=True" providerName = "System.Data.SqlClient"/>
  </connectionStrings>

实际上不需要添加EmpDBContext连接字符串。如果不指定连接字符串，则Entity Framework将使用DbContext类的标准名称在用户目录中创建localDB数据库。 对于这个示例，我们不会添加连接字符串来简化。

现在需要更新EmployeeController.cs文件，以便可以从数据库中保存和检索数据，而不是使用硬编码的数据。

首先，添加创建一个私有的EmpDBContext类对象，然后更新Index，Create和Edit动作方法，如下面的代码所示。参考以下代码 -

using MVCDatabaseAccess.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCDatabaseAccess.Controllers
{
    public class EmployeeController : Controller
    {
        private EmpDBContext db = new EmpDBContext();

        // GET: Employee
        public ActionResult Index()
        {
            var employees = from e in db.Employees
                            orderby e.ID
                            select e;
            return View(employees);
        }

        // GET: Employee/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Employee/Create
        [HttpPost]
        public ActionResult Create(Employee emp)
        {
            try
            {
                db.Employees.Add(emp);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        // GET: Employee/Edit/5
        public ActionResult Edit(int id)
        {
            var employee = db.Employees.Single(m => m.ID == id);
            return View(employee);
        }

        // POST: Employee/Edit/5
        [HttpPost]
        public ActionResult Edit(int id, FormCollection collection)
        {
            try
            {
                var employee = db.Employees.Single(m => m.ID == id);
                if (TryUpdateModel(employee))
                {
                    //To Do:- database code
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                return View(employee);
            }
            catch
            {
                return View();
            }
        }
    }
}

然后访问以下URL：http://localhost:61868/Employee运行这个应用程序。将看到以下输出。

正如所看到的，这个视图上没有数据，但是程序已经自动创建了一个表：Employee，这是因为我们还没有在数据库表中添加任何记录。

进入SQL Server对象资源管理器，会看到数据库使用与我们在DBContext类中创建的相同的名称 - Employee

展开这个数据库，会看到它有一个包含Employee模型类中的所有字段的表。

要查看此表中的数据，请右键单击Employees表并选择查看数据。应该会看到目前没有数据记录。我们直接在数据库的Employee表中添加一些记录，如下图所示 -

刷新浏览器，应该会看到数据现在已经更新到了视图中了。如下图所示 -

点击“Create New” 链接，从浏览器中添加一条记录，它将显示创建视图。在下面的字段中添加一些数据。

点击Create按钮，它会更新索引视图以及添加这个新的记录到数据库。

现在打开SQL Server对象资源管理器并刷新数据库。右键单击Employees表并选择查看数据菜单选项。应该就会看到该记录被添加到数据库中了。

ASP.Net MVC验证

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_validation.html

验证是ASP.NET MVC应用程序中的一个重要方面。 它用于检查用户输入是否有效。 ASP.NET MVC提供了一组易于使用的验证，同时也是检查错误并在必要时向用户显示消息的强大方法。

DRY

DRY代表(Don’t Repeat Yourself )不要重复自己，是ASP.NET MVC的核心设计原则之一。从开发的角度来看，鼓励只在一个地方指定功能或行为，然后在整个应用程序中使用它。
这样可以减少编写的代码量，使代码编写起来更容易，更容易维护。

为模型添加验证

下面来看看，如何为上一章项目添加员工时验证的一个简单例子。 在这个例子中，我们将数据注释添加到模型类，它提供了一些内置的验证属性集，可以直接在应用程序中应用到任何模型类或属性，例如：Required，StringLength，RegularExpression和Range验证属性。

它还包含格式化属性，如DataType帮助格式化，不提供任何验证。 验证属性指定要对其应用的模型属性强制执行的行为。

Required和MinimumLength属性指示属性必须具有值; 但是没有任何东西阻止用户输入空白格来满足这个验证。RegularExpression属性用于限制可输入的字符。

可以通过添加不同的注释属性来更新 Employee 类，如以下代码所示 -

还需要对数据库设置限制。 但是，SQL Server对象资源管理器中的数据库显示name属性被设置为NVARCHAR(MAX)，如以下截图所示 -

现在我们还需要对数据库设置限制。 但是，SQL Server对象资源管理器中的数据库显示name属性被设置为NVARCHAR(MAX)，如以下屏幕截图所示 -

为了在数据库上设置这个限制，我们将使用迁移来更新模式。

从 工具 -> NuGet程序包管理器 -> 程序包管理器控制台 打开程序包管理器控制台窗口。

在软件包管理器控制台窗口中逐个输入以下命令。

Enable-Migrations
add-migration DataAnnotations
update-database

以下是在 程序包管理器控制台 窗口中执行这些命令后的日志。

Visual Studio也将打开从DbMIgration类派生的类，可以在其中看到更新Up方法中的模式约束的代码 -

namespace MVCSimpleApp.Migrations {
   using System;
   using System.Data.Entity.Migrations;

   public partial class DataAnnotations : DbMigration{
      public override void Up(){
         AlterColumn("dbo.Employees", "Name", c => c.String(maxLength: 60));
      }

      public override void Down(){
         AlterColumn("dbo.Employees", "Name", c => c.String());
      }
   }
}

name字段的最大长度为60，这是数据库中新的长度限制，如以下快照中所示 -

运行此应用程序并通过指定以下URL转到创建视图：http://localhost:63004/Employee/Create -

在这些字段中输入一些无效的数据，然后点击 创建 按钮，如下图所示 -

看到jQuery客户端验证检测到错误，并且还显示错误消息。

ASP.Net MVC安全

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_security.html

在本章中，我们将讨论如何在应用程序中实现安全功能。还将看到ASP.NET中包含的新成员特性，并可从ASP.NET MVC中使用。在ASP.NET的最新版本中，可以通过以下方式管理用户身份 -

云

SQL数据库

本地Windows活动目录

在本章中，我们将介绍作为ASP.NET一部分的新身份组件，并了解如何自定义用户和角色的成员资格。

认证

用户的认证意味着验证用户的身份，这真的很重要。可能需要将您的应用程序仅显示给经过身份验证的用户，原因很明显。
我们来创建一个新的ASP.Net MVC应用程序项目：MVCSecurity 。点击确定 继续。

当启动一个新的ASP.NET应用程序时，这个过程中的一个步骤就是为应用程序需要配置身份验证服务。选择MVC模板，将看到现在启用了更改认证按钮。

这是通过出现在“新建项目”对话框中的“更改认证”按钮完成的。默认身份验证是个人用户帐户。

认证选项

当单击更改按钮时，您将看到一个对话框，其中包含四个选项，如下所示。

1. 不进行身份验证

第一个选项是不验证，当想建立一个不关心访问者是谁的网站时使用这个选项。

它是开放给任何人和每个人连接为每一个页面。以后可以随时更改，但“不进行身份验证” 选项意味着不会有任何功能来识别访问该网站的用户。

2. 个人用户帐户

第二个选项是个人用户帐户，这是用户可以访问网站的传统的基于表单的身份验证。 他们可以注册，创建一个登录名，默认情况下，用户名是使用一些新的ASP.NET身份特性存储在SQL Server数据库中。

密码也存储在数据库中，但首先被散列。由于密码是散列的，因此不必担心在数据库中的纯文本密码而被别人知道。

此选项通常用于要建立用户身份的Internet站点。 除了允许用户使用网站的密码创建本地登录外，还可以启用来自Microsoft，Google，Facebook和Twitter等第三方的登录。

这允许用户使用他们的真实帐户或他们的Twitter帐户登录到您的网站，但是您不需要存储任何密码。

这将在这个模块中花费一些时间的选项。个人用户帐户 选项。

3. 工作和学校帐户

第三个选择是使用组织帐户，这通常用于使用活动目录联合服务的业务应用程序。

将设置Office 365或使用Azure Active Directory服务，并且您有内部应用程序和云应用程序的单一登录。

您还需要提供应用程序ID，以便应用程序需要在Windows Azure管理门户(如果这是基于Azure的)上进行注册，并且应用程序ID将在所有可能注册的应用程序中唯一标识此应用程序。

4. Windows身份验证

第四个选项是Windows身份验证 ，适用于Intranet应用程序。

用户登录到Windows桌面，并可以将浏览器启动到位于同一防火墙内的应用程序。 ASP.NET可以自动获取用户的身份，即由活动目录建立的身份。 该选项不允许任何匿名访问该站点，但这也是一个可以更改的配置设置。

下面我们来看看基于表单的身份验证 ，即名称为个人用户帐户的身份验证。 此应用程序将用户名和密码，旧密码存储在本地SQL Server数据库中，创建此项目时，Visual Studio也将添加NuGet包。

现在运行这个应用程序，当您第一次访问这个应用程序，那么是以一个匿名用户来访问的。

因为您还没有登录的账户，所以需要在这个网站上先注册一个用户。

点击注册 链接，会看到下面的视图。

输入您的电子邮件ID和密码，例如：maxsu@yiibai.com和Abc@123。

点击注册。 现在，应用程序将使用此账户信息识别您。

它将能够显示用户的名字。 在下面的截图中，可以看到：“你好，maxsu@yiibai.com！” 。 可以点击它链接到一个页面，可以在这个页面中更改密码。

也可以注销，关闭，重新启动，一个星期后回来，应该可以使用之前使用的凭据登录。现在点击注销 按钮，它将显示以下页面。再次点击登录链接进入下一页。可以使用相同的凭据重新登录。

很多工作都在幕后进行到现在。 但是，我们想要做的是检查每个功能，看看这个UI是如何构建的。 什么是管理注销和登录过程？ 这些信息在数据库中排序？

让我们从一些简单的基础开始。 首先，我们将看到这个用户名是如何显示的。 从解决方案资源管理器中的View/Shared文件夹中打开_Layout.cshtml。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")

</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("应用程序名称", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("主页", "Index", "Home")</li>
                    <li>@Html.ActionLink("关于", "About", "Home")</li>
                    <li>@Html.ActionLink("联系方式", "Contact", "Home")</li>
                </ul>
                @Html.Partial("_LoginPartial")
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - 我的 ASP.NET 应用程序</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

有一个共同的导航栏，应用程序名称，菜单，并有一个局部视图呈现为_loginpartial。 这实际上是显示用户名或注册和登录名的视图。 所以_loginpartial.cshtml也在shared文件夹中。其代码如下所示 -

@using Microsoft.AspNet.Identity
@if (Request.IsAuthenticated)
{
    using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
    {
    @Html.AntiForgeryToken()

    <ul class="nav navbar-nav navbar-right">
        <li>
            @Html.ActionLink("你好，" + User.Identity.GetUserName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
        </li>
        <li><a href="javascript:document.getElementById('logoutForm').submit()">注销</a></li>
    </ul>
    }
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("注册", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
        <li>@Html.ActionLink("登录", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
    </ul>
}

正如可以看到上面，有if/else语句。 如果请求没有被认证，这个视图将显示注册和登录链接。用户可以点击链接登录或注册。所有这些都是由帐户控制器完成的。

现在，我们想看看如何获取用户名，这是在Request.IsAuthenticated。 可以看到对User.Identity.GetUserName的调用。这将检索用户名，在这种情况下是"maxsu@yiibai.com"

授权

假设想要防止未经验证的用户的信息。 因此，让我们创建一个新的控制器来显示这些信息，但只有当用户登录时才能操作。

右键单击Controllers 文件夹，然后选择：添加 -> 控制器 。选择一个MVC 5控制器 - 空 控制器，然后点击“添加”。输入名称SecretController，然后单击“添加” 按钮。

它将会有两个动作，如下面的代码所示。参考以下代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCSecurity.Controllers
{
    public class SecretController : Controller
    {
        // GET: Secret
        public ActionResult Index()
        {
            return View();
        }
        // GET: Secret
        public ContentResult Secret()
        {
            return Content("Secret informations here");
        }

        public ContentResult PublicInfo()
        {
            return Content("Public informations here");
        }
    }
}

当运行这个应用程序时，可以在没有任何验证的情况下访问这个信息(URL:http://localhost:57742/Secret/Secret)，如下面的截图所示。

假设只有经过身份验证的用户才能够使用Secret动作方法，并且任何人都可以使用PublicInfo，而不需要任何身份验证。

为了保护这个特定的操作并保证未经身份验证的用户到达此处，可以使用Authorize属性。 没有任何其他参数的授权属性将确保用户的身份是已知的，他们不是一个匿名用户。

// GET: Secret
[Authorize]
public ContentResult Secret(){
   return Content("Secret informations here");
}

现在再次运行这个应用程序，并指定相同的URL：http://localhost:57742/Secret/Secret。 MVC应用程序将检测到您无权访问应用程序的特定区域，并且会自动重定向到登录页面，在那里登录并尝试返回访问受限的应用程序的URL。

可以看到它在返回URL中指定，它告诉此页面，如果用户成功登录，则将其重定向到/secret/secret。

输入您的用户名和密码，然后点击“登录”按钮。会看到它直接进入该页面。

当不想在每一个动作上进行授权的时候，当想要在一个控制器里，几乎所有的事情都需要授权。 在这种情况下，总是可以将此过滤器应用于控制器本身，现在，此控制器内部的每个操作都将要求用户进行身份验证。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }

      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

但是，如果想要任何人都可以打开某一个操作，则可以使用另一个属性(即AllowAnonymous)覆盖此授权规则。参考以下代码 -

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }

      [AllowAnonymous]
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

使用Authorize属性，也可以指定一些参数，比如允许一些特定的用户进入这个动作。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize(Users = "maxsu@yiibai.com")]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }

      [AllowAnonymous]
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

当运行此应用程序并转到URL:/secret/secret时，如果它不是此控制器要求的用户(maxsu@yiibai.com)，它会要求您登录。

ASP.Net MVC缓存

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_caching.html

在本章中，我们将重点介绍最常用的ASP.NET技术之一：缓存，以提高应用程序的性能。 在内存中存储经常使用的内容以提供更好的性能。我们将看到如何通过利用输出缓存来显着提高ASP.NET MVC应用程序的性能。

在ASP.NET MVC中，可以应用OutputCache筛选器属性，这与Web表单中输出缓存的概念相同。 输出缓存使您能够缓存控制器操作返回的内容。

输出缓存基本上允许您将特定控制器的输出存储在内存中。 因此，来自该控制器中的相同动作的未来请求将从缓存结果中返回。 这样，每次调用相同的控制器操作时，都不需要生成相同的内容。

为什么要缓存？

我们需要缓存在许多不同的场景来提高应用程序的性能。 例如，一个ASP.NET MVC应用程序，它显示一个雇员名单。 现在，当每次用户调用控制器动作时，通过执行数据库查询从数据库中检索这些记录时，它将返回显示到Index视图中。

因此，可以利用输出缓存来避免每次调用相同的控制器操作时执行数据库查询。 在这种情况下，将从缓存中检索视图，而不是从控制器操作重新生成。

缓存使您可以避免在服务器上执行冗余工作。

下面来看看如何在项目中使用缓存的一个简单示例，为了方便演示，这里创建一个MVC Web项目：MVCCaching 。

注意：创建读取数据库表记录请参考：http://www.yiibai.com/asp.net_mvc/asp.net_mvc_databases.html

参考以下代码 -

using MVCCaching.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCCaching.Controllers
{
    public class EmployeeController : Controller
    {
        private EmpDBContext db = new EmpDBContext();
        // GET: Employee 使用缓存
        [OutputCache(Duration = 60)]
        public ActionResult Index()
        {
            var employees = from e in db.Employees
                            orderby e.ID
                            select e;
            return View(employees);
        }
        ... ...
    }
}

如您所见，在EmployeeController的Index动作上添加了“OutputCache”属性。 现在理解这个概念，在调试器模式下运行这个应用程序，并在Index操作方法中插入一个断点。

按‘F5’按钮继续，将看到视图中有从数据库中检索到的员工列表。

在60秒内再次刷新浏览器，会看到这个断点没有经过。 这是因为使用了输出缓存，持续时间为几秒。 所以它会把这个结果缓存60秒，当刷新浏览器时，它会从缓存中读取得到结果，而不会从数据库服务器加载内容。

除了持续时间参数之外，还有其他一些设置选项，可以使用输出缓存。 这些设置不仅适用于MVC框架，而且是从ASP.Net缓存继承的。

改变输出缓存

在某些情况下，可能需要不同的缓存版本，例如，当创建详细信息页面时，当单击详细链接时，将获得所选员工的详细信息。

但首先需要创建详细视图。 为此，请右键单击EmployeeController中的Details操作方法，然后选择添加视图…

在默认情况下选择了详细信息名称。现在从“模板”下拉列表中选择Details，从“模型”下拉列表中选择Employee -

点击“添加”继续，会看到生成了文件：Details.cshtml。参考以下代码 -

@model MVCCaching.Models.Employee

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Details</title>
</head>
<body>
    <div>
        <h4>Employee</h4>
        <hr />
        <dl class="dl-horizontal">
            <dt>
                @Html.DisplayNameFor(model => model.Name)
            </dt>

            <dd>
                @Html.DisplayFor(model => model.Name)
            </dd>

            <dt>
                @Html.DisplayNameFor(model => model.JoiningDate)
            </dt>

            <dd>
                @Html.DisplayFor(model => model.JoiningDate)
            </dd>

            <dt>
                @Html.DisplayNameFor(model => model.Age)
            </dt>

            <dd>
                @Html.DisplayFor(model => model.Age)
            </dd>

        </dl>
    </div>
    <p>
        @Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
        @Html.ActionLink("Back to List", "Index")
    </p>
</body>
</html>

可以利用[OutputCache]属性中的VaryByParam属性。这个属性使您能够在表单参数或查询字符串参数发生变化时创建不同的缓存版本。 以下是Details操作的实现。参考以下代码 -

// GET: Employee/Details/5
[OutputCache(Duration = int.MaxValue, VaryByParam = "id")]

public ActionResult Details(int id){
   var employee = db.Employees.SingleOrDefault(e => e.ID == id);
   return View(employee);
}

当编译并执行上述代码时，访问指定URL：http://localhost:59893/employee 应该会得到以下输出。

点击任何一个详细信息链接，将看到该特定员工的详细信息视图。

Details()操作包含值为“Id”的VaryByParam属性。 当Id参数的不同值传递给控制器操作时，会生成不同的缓存版本的Details视图。

理解使用VaryByParam属性导致更多缓存是非常重要的。 为每个不同版本的Id参数创建不同的缓存版本的Details视图。

缓存配置文件

可以在web.config文件中创建缓存配置文件。 可以通过修改[OutputCache]属性来配置输出缓存属性。 它提供了几个重要的优点，如下所示。

控制控制器如何在一个中心位置缓存内容。

创建一个缓存配置文件并将该配置文件应用于多个控制器或控制器操作。

修改网页配置文件，而不用重新编译应用程序。

为已经部署到生产环境的应用程序禁用缓存。

我们来看一个简单的缓存配置文件示例，在web.config文件中创建缓存配置文件。 <caching>部分必须出现在<system.web>部分中。如下 -

<caching>
   <outputCacheSettings>
      <outputCacheProfiles>
         <add name = "Cache10Min" duration = "600" varyByParam = "none"/>
      </outputCacheProfiles>
   </outputCacheSettings>
</caching>

可以使用[OutputCache]属性将Cache10Min配置文件应用于控制器操作，如下所示。

[OutputCache(CacheProfile = "Cache10Min")]

public ActionResult Index(){
   var employees = from e in db.Employees
   orderby e.ID
   select e;
   return View(employees);
}

当编译并执行上述代码时，访问指定URL：http://localhost:63004/employee 应该会得到以下输出。

如果调用Index()动作如上所示，那么同样也将是缓存10分钟。

ASP.Net MVC Razor

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_razor.html

在本章中，我们来看看ASP.NET MVC应用程序中的Razor视图引擎以及为什么使用Razor的一些原因。 Razor是一种标记语法，可以让您使用 C# 和VB.Net将基于服务器的代码嵌入到网页中。 这不是一种编程语言。 这是一个服务器端标记语言。

Razor与ASP.NET MVC没有任何关系，因为Razor是一个通用的模板引擎。 您可以在任何地方使用它来生成像HTML一样的输出。 只是ASP.NET MVC实现了一个视图引擎，它允许在MVC应用程序内部使用Razor来生成HTML。

假设有一个模板文件，它是一些文字和一些代码块的混合体。可以将该模板与一些数据或模板指定数据应该出现的位置的特定模型相结合，然后执行模板以生成输出。

Razor Vs ASPX

Razor与ASPX文件的工作方式非常相似。ASPX文件是模板，其中包含文本文本和一些 C# 代码，指定数据应该出现的位置。执行这些来为应用程序生成HTML。

ASPX文件依赖ASP.NET运行时来解析和执行这些ASPX文件。Razor没有这样的依赖关系。

与ASPX文件不同，Razor有一些不同的设计目标。

目标

微软希望Razor易于使用，易于学习，并且可以在Visual Studio等工具中使用，因此IntelliSense 和调试器可用，但他们希望Razor与特定技术(如ASP.NET或 ASP.NET MVC)绑定关联。

如果熟悉ASPX文件的生命周期，那么可能会意识到，ASP.NET运行时可以解析和执行这些ASPX文件。 微软希望Razor聪明，让开发人员的工作更轻松。

下面来看一个ASPX文件中的示例代码，它包含一些文本文本。这是HTML标记，包含一点 C# 代码。

<% foreach (var item in Model) { %>
   <tr>
      <td>
         <%: Html.ActionLink("Edit", "Edit", new { id = item.ID })%> |
         <%: Html.ActionLink("Details", "Details", new { id = item.ID }) %>|
         <%: Html.ActionLink("Delete", "Delete", new { id = item.ID })%>
      </td>

      <td>
         <%: item.Name %>
      </td>

      <td>
         <%: String.Format("{0,g}", item.JoiningDate) %>
      </td>

   </tr>
<%}%>

但是这些Web表单基本上是由微软重新使用的，与MVC的早期版本一起工作，这意味着ASPX文件从来不是MVC的完美搭配。

当您需要从 C# 代码转换回HTML代码，或者从HTML代码转换回 C# 代码时，语法有点笨拙。 IntelliSense也会提示您执行一些在MVC项目中没有意义的事情，例如将输出缓存和用户控件的指令添加到ASPX视图中。

现在看看这个产生相同输出的代码，不同之处在于它使用的是Razor语法。

@foreach (var item in Model) {
   <tr>
      <td>
         @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
         @Html.ActionLink("Details", "Details", new { id = item.ID }) |
         @Html.ActionLink("Delete", "Delete", new { id = item.ID })
      </td>

      <td>
         @item.Name
      </td>

      <td>
         @String.Format("{0,g}", item.JoiningDate)
      </td>
   </tr>
}

使用Razor语法，可以使用'@'符号开始编写 C# 代码，Razor解析会自动切换到解析这个语句，这个foreach语句，解析作为 C# 代码。

但是，当我们完成了foreach语句，并且使用大括号的时候，可以从 C# 代码转换成HTML，而不用在里面放置一个明确的标记，就像尖括号中的百分比。

Razor解析器足够聪明，可以在 C# 代码和HTML之间进行切换，而当将大括号放在这里时，再次从HTML切换回 C# 代码。如果比较这两个代码块，您会发现Razor版本更易于阅读和编写。

使用Razor创建视图

下面我们来创建一个新的ASP.Net MVC项目。

在名称字段中输入项目的名称：MVCRazor，然后单击确定。如下图所示 -

为了简单起见，选择 空 选项并在为以下项添加文件夹和核心引用 部分选中MVC复选框，然后单击确定。 它将使用最少的预定义内容创建一个基本的MVC项目。

项目创建后，将在“解决方案资源管理器”窗口中看到许多文件和文件夹。 因为我们已经从一个空的项目模板创建了ASP.Net MVC项目，所以目前应用程序不包含任何要运行的东西。 由于从一个空应用程序开始，甚至没有一个控制器，所以需要手动添加一个控制器：HomeController 。

要添加控制器，请右键单击解决方案资源管理器 中的Controllers 文件夹，然后选择:添加 -> 控制器 ，它将显示“添加基架” 对话框。

选择MVC 5控制器 - 空 选项，然后单击添加 按钮，然后添加控制器对话框将出现。

将名称设置为：HomeController，然后单击“添加”按钮。在Controllers 文件夹中看到一个新的 C# 文件：HomeController.cs，在Visual Studio中打开并进行编辑。代码如下所示 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCRazor.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
    }
}

右键单击Index操作方法，然后选择添加视图… ，如下所示 -

从模板下拉列表中选择Empty，然后点击添加 按钮。 Visual Studio将在View/Home文件夹中创建一个Index.cshtml文件。内容如下所示 -

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
    </div>
</body>
</html>

请注意，Razor视图有一个cshtml扩展名。 如果使用Visual Basic构建MVC应用程序，它将是一个VBHTML扩展。在这个文件的顶部是一个代码块，它将这个Layout属性显式地设置为null。

当运行这个应用程序，会看到空白的网页，因为我们已经创建了一个空模板的视图。

下面添加一些 C# 代码。要在Razor视图中编写一些 C# 代码，要做的第一件事就是输入"@"符号，告诉解析器它将在代码中执行某些操作。

创建一个FOR循环，在大括号内指定@index，这实质上是告诉 Razor 存储变量index的值。

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width = device-width" />
    <title>第一个Razor示例</title>
</head>

<body>
    <div>
        @for (int index = 0; index < 12; index++)
        {
            <div>@index </div>
        }
    </div>
</body>

</html>

运行这个应用程序，会看到下面的输出。

ASP.Net MVC数据注解

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_data_annotations.html

DataAnnotations用于配置模型类，它将突出显示最常用的配置。 DataAnnotations也被许多.NET应用程序所理解，例如ASP.NET MVC，它允许这些应用程序利用相同的注释来进行客户端验证。DataAnnotation属性重写默认的Code-First约定。

System.ComponentModel.DataAnnotations包括以下影响列的可空性或大小的属性。

Key

Timestamp

ConcurrencyCheck

Required

MinLength

MaxLength

StringLength

System.ComponentModel.DataAnnotations.Schema命名空间包括以下影响数据库模式的属性。

Table

Column

Index

ForeignKey

NotMapped

InverseProperty

1. 键(Key)

实体框架(Entity Framework或简称为EF )依赖于具有用于跟踪实体的键值的每个实体。 Code First依赖的其中一个约定是它如何暗示哪个属性是每个Code First类中的键。

约定是寻找一个名为Id的属性，或者将类名和Id结合起来的属性，比如StudentId。 该属性将映射到数据库中的主键列。学生，课程和入学课程遵循这个约定。

现在让假设Student类使用名称StdntID而不是ID。 当Code First找不到符合此约定的属性时，它将抛出一个异常，因为Entity Framework要求必须具有一个键属性。

可以使用键注释来指定哪个属性将被用作EntityKey。

下面来看看包含StdntID的Student类。 它不遵循默认的Code First约定，所以要处理这个问题，添加了Key属性，使StdntID列成为主键。

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

在运行应用程序并查看SQL Server资源管理器中的数据库时，您将看到现在Students表中的主键是：StdntID。

实体框架(Entity Framework)也支持复合键。 复合键是由多个属性组成的主键。例如，有一个DrivingLicense类，其主键是LicenseNumber和IssuingCountry的组合。

public class DrivingLicense{
   [Key, Column(Order = 1)]
   public int LicenseNumber { get; set; }

   [Key, Column(Order = 2)]
   public string IssuingCountry { get; set; }
   public DateTime Issued { get; set; }
   public DateTime Expires { get; set; }
}

当有组合键时，实体框架要求你定义键属性的顺序。可以使用Column注释来指定顺序。

2. 时间戳(Timestamp)

Code First会将Timestamp属性视为ConcurrencyCheck属性，但它也将确保Code First生成的数据库字段不可空。

使用rowversion或timestamp字段进行并发检查更为常见。但是，不要使用ConcurrencyCheck注释，只要属性的类型是字节数组，就可以使用更具体的TimeStamp注释。在给定的类中只能有一个时间戳属性。

下面来看一个简单的例子，将TimeStamp属性添加到Course类中。参考以下代码 -

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp]
   public byte[] TStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

如上例所示，Timestamp属性应用于Course类的Byte []属性。 所以，Code First 将在Courses表中创建一个时间戳列TStamp。

3. ConcurrencyCheck

ConcurrencyCheck注释允许在用户编辑或删除实体时标记一个或多个要用于数据库并发检查的属性。如果一直在使用EF Designer，那么这将与将属性的“并发性模式”设置为“固定”一致。

下面来看看一个简单的例子，通过将它添加Title属性到Course类中来了解ConcurrencyCheck是如何工作的。

public class Course{
   public int CourseID { get; set; }

   [ConcurrencyCheck]
   public string Title { get; set; }
   public int Credits { get; set; }

   [Timestamp, DataType("timestamp")]
   public byte[] TimeStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

在上面的Course类中，ConcurrencyCheck属性应用于现有的Title属性。 Code First将在update命令中包含Title列来检查以下代码所示的乐观并发。

exec sp_executesql N'UPDATE [dbo].[Courses]
   SET [Title] = @0
   WHERE (([CourseID] = @1) AND ([Title] = @2))
   ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max)
',@0 = N'Maths',@1 = 1,@2 = N'Calculus'
go

4. Required

Required注释告诉实体框架(Entity Framework)需要一个特定的属性。下面来看看Student类，其中必需的id被添加到FirstMidName属性中。 Required属性将强制Entity Framework 确保该属性中包含数据。

public class Student{
   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

可以在上面的Student类的示例中看到Required属性应用于FirstMidName和LastName。 因此，Code First将在Students表中创建一个NOT NULL的 FirstMidName和LastName列，如下图所示。

5. MaxLength

MaxLength属性用于指定其他属性验证。它可以应用于类的字符串或数组类型的属性。 Entity Framework的 Code First 将设置MaxLength属性中指定的列的大小。

下面来看看MaxLength(24)属性应用于Title属性的以下Course类。

public class Course{
   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24)]

   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

当运行上述应用程序时，Code-First将在Coursed表中创建一个nvarchar(24)列标题，如以下屏幕截图所示。

现在当用户设置包含超过24个字符的标题时，Entity Framework将抛出EntityValidationError异常。

6. MinLength

MinLength属性可指定其他属性验证，就像上面使用的MaxLength属性一样。 MinLength属性也可以与MaxLength属性一起使用，如下面的代码所示。

public class Course{
   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24) , MinLength(5)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

如果在MinLength属性中将Title属性的值设置为小于指定的长度或大于MaxLength属性中的指定长度，则EF将抛出EntityValidationError异常。

7. StringLength

StringLength还允许指定其他属性验证，如MaxLength。 不同的是StringLength属性只能应用于Domain类的字符串类型属性。参考以下示例代码 -

public class Course{
   public int CourseID { get; set; }
   [StringLength (24)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Entity Framework还验证StringLength属性的属性值。 现在，如果用户设置标题(Title)，其中包含超过24个字符，那么EF将抛出EntityValidationError异常。

8. Table

默认代码第一个约定创建一个与类名相同的表名。 如果让Code First创建数据库，则还可以更改正在创建的表的名称。可以让Code First使用现有的数据库表。 但并不总是这样，有时类的名称与数据库中表的名称不能总是相匹配。

Table属性重写此默认约定。 对于给定的域类，EF Code First将在Table属性使用指定的名称来创建一个表。

下面来看看一个类名为Student的例子，按照惯例，Code First假定这将映射到一个名为Students的表。 如果不是这种情况，可以使用Table属性指定表的名称，如以下代码所示 指定要创建的表名称为：StudentInfo -

[Table("StudentsInfo")]
public class Student{
   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

现在可以看到Table属性将表指定为StudentsInfo。 生成表时，如下图所示的表名StudentInfo。

不仅可以指定表名，还可以使用以下代码使用Table属性指定表的模式。

[Table("StudentsInfo", Schema = "Admin")]

public class Student{
   [Key]
   public int StdntID { get; set; }

   [Required]
   public string LastName { get; set; }

   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

在上面的例子中，表被指定为Admin模式。 现在，Code First将在Admin模式中创建StudentsInfo表，如以下屏幕截图所示。

9. Column

Column属性也与Table属性相同，但Table属性覆盖表行为，而Column属性覆盖列行为。 默认代码第一个约定创建一个与属性名相同的列名。

如果让Code First创建数据库，并且还希望更改表中列的名称。Column属性重写此默认约定。 EF Code First将在给定类属性的Column属性中创建一个具有指定名称的列。

下面来看看下面的例子，其中属性名为FirstMidName，按照惯例，Code First假定这将映射到一个名为FirstMidName的列。 如果不是要映射到FirstMidName列时，可以使用Column属性指定其它列的名称，如以下代码所示。

public class Student{
   public int ID { get; set; }
   public string LastName { get; set; }

   [Column("FirstName")]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

现在可以看到Column属性将列指定为FirstName。 生成表后，可以看到列名为FirstName，如以下屏幕截图所示。

10. Index

Index属性是在Entity Framework 6.1中引入的。

注 - 如果您使用的是早期版本，则本节中的信息不适用。

可以使用Index属性在一列或多列上创建索引。将属性添加到一个或多个属性将导致EF在创建数据库时在数据库中创建相应的索引。

在大多数情况下，索引使数据的检索更快，更高效。但是，使用索引重载表或视图可能会不愉快地影响其他操作(如插入或更新)的性能。

索引是实体框架中的新功能，可以通过减少从数据库查询数据所需的时间来提高Code First应用程序的性能。

可以使用Index属性将索引添加到数据库，并覆盖默认的“唯一”和“群集”设置，以获得最适合您的方案的索引。默认情况下，索引将被命名为IX_<属性名称>

让我们来看看以下代码，其中Index属性被添加到Course类Credits列上。

public class Cours{
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

可以看到Index属性应用于Credits属性。 现在，当表生成时，将在索引中看到名称为IX_Credits的索引。

默认情况下，索引是非唯一的，但是可以使用IsUnique命名参数来指定索引应该是唯一的。 以下示例引入了一个唯一索引，如下面的代码所示。

public class Course{
   public int CourseID { get; set; }
   [Index(IsUnique = true)]

   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

11. ForeignKey

Code First约定将处理模型中最常见的关系。 例如，通过更改Student类中的key属性名称，创建了与Enrollment类的关系问题。

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

在生成数据库时，Code First会在Enrollment类中看到StudentID属性，并按照约定将其识别为类名称加ID，作为Student类的外键。但是Student类中没有StudentID属性，而是Student类中的StdntID属性。

解决方法是在Enrollment中创建导航属性，并使用ForeignKey DataAnnotation来帮助Code First了解如何构建两个类之间的关系，如以下代码所示。

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }

   [ForeignKey("StudentID")]
   public virtual Student Student { get; set; }
}

现在可以看到ForeignKey属性应用于导航属性。

12. NotMapped

Code First的约定在默认情况下，每个属于受支持数据类型的属性都包含getter和setter，它们在数据库中表示。 但是在应用中并不总是这样。 NotMapped属性将覆盖此默认约定。 例如，可能在Student类中有一个属性，例如FatherName，但不需要存储它到数据库表。 那么可以将NotMapped属性应用于您不希望在数据库中创建列的FatherName属性。 以下是代码。

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
   [NotMapped]
   public int FatherName { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

可以看到NotMapped属性应用于FatherName属性。 现在，当生成表时，将看到FatherName列不会在数据库中创建，但它存在于Student类中。

Code First 不会为没有getter或setter的属性创建一个列。

13. InverseProperty

在类之间有多个关系时使用InverseProperty。 在Enrollment类中，可能想要跟踪注册“当前课程”的人员和注册“以前课程”的人员。

我们为Enrollment类添加两个导航属性。

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   public virtual Course CurrCourse { get; set; }
   public virtual Course PrevCourse { get; set; }
   public virtual Student Student { get; set; }
}

同样，还需要添加这些属性引用Course类。 Course类的导航属性返回到Enrollment类，其中包含当前和以前的所有注册。

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]

   public int Credits { get; set; }
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

如果外键属性未包含在上述类中所示的特定类中，Code First会创建{Class Name} _ {Primary Key}外键列。 生成数据库表时，您将看到许多外键，如以下屏幕截图所示。

正如所看到的Code First 无法自己匹配两个类的属性。 用于Enrollments的数据库表应该有一个用于CurrCourse的外键和一个用于PrevCourse的外键，但Code First 将创建四个外键属性，即 -

CurrCourse_CourseID

PrevCourse_CourseID

Course_CourseID

Course_CourseID1

要解决这些问题，可以使用InverseProperty注解来指定属性的对齐方式。

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }

   [Index]
   public int Credits { get; set; }

   [InverseProperty("CurrCourse")]
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }

   [InverseProperty("PrevCourse")]
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

正如上面所看到的那样，当InverseProperty属性通过指定它所属的Enrollment类的哪个引用属性应用于上述Course类时，Code First将生成数据库表，并在Enrollments表中创建两个外键列，如以下屏幕截图所示。

我们建议执行上述示例以便更好地理解。

ASP.Net MVC NuGet包管理

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_nuget_package_management.html

在本章中，我们将讨论NuGet，它是.NET和Visual Studio的包管理器。 NuGet可以用来查找和安装软件包，也就是软件和程序集以及想在项目中使用的东西。

NuGet不是一个特定于ASP.NET MVC项目的工具。 它是一个工具，可以在Visual Studio中使用控制台应用程序，WPF应用程序，Azure应用程序以及任何类型的应用程序。

包管理

NuGet是一个软件包管理器，负责在系统中下载，安装，更新和配置软件。 从术语软件来说，我们并不是指像Microsoft Word或Notepad 2等最终用户软件，而是在项目中使用的软件，程序集引用。

例如，想要使用的程序集可能是模拟的，模拟对象单元测试，NHibernate的数据访问，以及在构建应用程序时使用的组件。 上述组件是开源软件，但是发现一些NuGet 软件包是封闭源码软件。您会发现一些软件包甚至是由微软生产的。

如上面提到的所有软件包(如mock和NHibernate)和Microsoft软件包(如Entity Framework的预览版)在默认情况下它们不包含在Visual Studio中。

不使用NuGet

要在没有NuGet的情况下安装这些组件，您将需要经过以下步骤。

如果想使用某一个组件，首先需要找到一些特定项目(组件)的主页，并寻找下载链接。 然后项目(组件)下载完成后，它通常是一个ZIP格式，所以需要解压提取它。

如果没有下载二进制文件，那么首先需要构建该软件，然后在您的项目中引用它。而且这个时候的许多组件还需要一些配置来启动和运行。

使用NuGet

NuGet取代了前面讨论过的所有步骤，只需要“添加软件包”即可。 NuGet知道如何下载最新版本，知道如何提取它，如何建立对该组件的引用，甚至配置它。 这让您有更多时间来构建和开发软件代码。

下面来看一个简单的例子，我们将使用NuGet在ASP.NET MVC项目中添加对Entity框架的支持。

第1步 - 安装实体框架(Entity Framework)。 右键单击该项目，然后选择管理NuGet程序包 -> NuGet包管理器 以获得解决方案…，它将打开NuGet包管理器。

第2步 - 在搜索框中搜索实体框架 - Entity Framework ，结果如下-

第3步 - 选择实体框架(Entity Framework)，然后点击“安装”按钮。 它将打开预览对话框。

接受协议，如下图所示 -

第4步 - 点击“我接受”按钮开始安装。

当安装了实体框架，将看到如上所示的消息。

当使用NuGet安装软件包时，会在托管项目的解决方案文件所在的文件夹中看到新的软件包目录。此软件包目录包含为该解决方案中的任何项目安装的所有软件包。

换句话说，NuGet不会将软件包下载到中央位置，而是将其存储在每个解决方案之中。

ASP.Net MVC Web API

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_web_api.html

ASP.NET Web API是一个框架，可以轻松构建到达各种客户端(包括浏览器和移动设备)的HTTP服务。ASP.NET Web API是在.NET Framework上构建RESTful应用程序的理想平台。

当要Web上构建API时，可以通过多种方式在Web上构建API。 这些包括HTTP/RPC，这意味着在远程过程调用中使用HTTP来通过Web调用诸如方法之类的东西。

这些动词本身包含在API中，例如：获取客户，插入发票，删除客户，并且每个这些端点最终都是一个单独的URI。

下面通过创建一个新的ASP.NET Web应用程序来看看如何构造一个简单的Web API示例。

第1步 - 打开Visual Studio，然后单击：文件 -> 新建 -> 项目 菜单选项。
一个新的项目对话框打开。如下图所示 -

第2步 - 在左侧窗格中，选择：模板 -> Visual C# -> Web 。

第3步 - 在中间窗格中，选择“ASP.NET Web应用程序”，在名称字段中输入项目名称：WebAPIDemo ，然后单击确定 以继续。将看到以下对话框，要求您为ASP.NET项目设置初始内容。如下图所示 -

第4步 - 为了简单起见，请选择: 空 选项，并在“为以下项添加文件夹和核心引用” 部分中选中Web API 复选框，然后单击 确定 。

第5步 - 它将创建一个基本的MVC项目与最小的预定义的内容。

项目由Visual Studio创建后，在“解决方案资源管理器”窗口中看到许多文件和文件夹。

第6步 - 现在需要添加一个模型。右键单击解决方案资源管理器 中的Models 文件夹，然后选择：添加 -> 类。将看到添加新项目对话框。
第7步 - 选择中间平台的类，并在名称字段中输入Employee.cs。

第8步 - 使用下面的代码将一些属性添加到Employee类。代码如下所示 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebAPIDemo.Models
{
    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime JoiningDate { get; set; }
        public int Age { get; set; }
    }
}

第9步 - 添加控制器。右键单击解决方案资源管理器 中的Controllers 文件夹，然后选择：添加 -> 控制器 。它将显示“添加基架”对话框 -

第10步 - 选择：Web API 2控制器 - 空 选项。该模板将为控制器创建一个具有默认操作的Index方法。

第11步 - 点击“添加”按钮，添加控制器对话框将出现。
第12步 - 将名称设置为：EmployeesController，然后单击“添加” 按钮。

在Controllers文件夹中看到一个新的 C# 文件 - EmployeeController.cs，该文件夹在Visual Studio中打开，并进行一些默认操作。如下代码 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebAPIDemo.Models;

namespace WebAPIDemo.Controllers
{
    public class EmployeesController : ApiController
    {
        Employee[] employees = new Employee[]{
         new Employee { ID = 1, Name = "Maxsu", JoiningDate =
            DateTime.Parse(DateTime.Today.ToString()), Age = 30 },
         new Employee { ID = 2, Name = "Allan", JoiningDate =
            DateTime.Parse(DateTime.Today.ToString()), Age = 35 },
         new Employee { ID = 3, Name = "AvgWong", JoiningDate =
            DateTime.Parse(DateTime.Today.ToString()), Age = 21 }
      };

        public IEnumerable<Employee> GetAllEmployees()
        {
            return employees;
        }

        public IHttpActionResult GetEmployee(int id)
        {
            var employee = employees.FirstOrDefault((p) => p.ID == id);
            if (employee == null)
            {
                return NotFound();
            }
            return Ok(employee);
        }
    }
}

第13步 - 运行这个应用程序，并在浏览器中访问URL：http://localhost:58150/api/employees/ ，然后按“Enter”。将看到以下输出 -

第14步 - 再次访问URL：http://localhost:58150/api/employees/1 ，将看到以下输出 -

ASP.Net MVC手脚架

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_scaffolding.html

ASP.NET脚手架是ASP.NET Web应用程序的代码生成框架。 Visual Studio 2013包含用于MVC和Web API项目的预安装代码生成器。当想快速添加与数据模型交互的代码时，可以将脚手架添加到项目中。使用脚手架可以减少在项目中开发标准数据操作的时间。

正如在前面章节教程中所看到的，我们已经为Index，Create，Edit操作创建了视图，并且还需要更新操作方法。但ASP.Net MVC提供了一种使用脚手架创建所有这些视图和操作方法的更简单的方法。

我们来看一个简单的例子。它创建一个包含模型类 - Employee的相同示例，但是这次将使用脚手架。

第1步 - 打开Visual Studio，然后单击：文件 -> 新建 -> 项目 菜单选项。一个新的项目对话框打开。
第2步 - 在左侧窗格中，选择：模板 -> Visual C# -> Web 。
第3步 - 在中间窗格中，选择“ASP.NET Web应用程序”。
第4步 - 在名称字段中输入项目名称：MVCScaffolding ，然后单击确定 继续。

将看到下面的对话框，要求设置ASP.NET项目的初始内容。

第5步 - 为了简单起见，选择：空 选项，并在为以下项添加文件夹和核心引用’ 中选中MVC 复选框，然后单击确定。

它将使用最少的预定义内容创建一个基本的MVC项目。

项目由Visual Studio创建后，将在“解决方案资源管理器” 窗口中看到许多文件和文件夹。

添加 Entity Framework 支持

第一步是安装实体框架(Entity Framework)。右键单击该项目，然后选择：管理NuGet程序包 ，打开NuGet软件包管理器 。它将打开“NuGet包管理器”，在搜索框中搜索Entity Framework。如下所示 -

选择实体框架(Entity Framework)，然后点击“安装(Install)” 按钮。 它将打开预览对话框。

点击确定 继续下一步 -

点击“我接受”按钮开始安装，完成后如下 -

当实体框架安装成功后，可看到如上面的截图所示的出窗口的消息。

添加模型

要添加模型，请右键单击解决方案资源管理器 中的Models 文件夹，然后选择：添加 -> 类 ，会看到添加新项目 对话框。

在中间平移中选择：类，并在名称字段中输入：Employee.cs。如下图所示 -

使用以下代码将一些属性添加到Employee类。

using System;

namespace MVCScaffolding.Models {
   public class Employee{
      public int ID { get; set; }
      public string Name { get; set; }
      public DateTime JoiningDate { get; set; }
      public int Age { get; set; }
   }
}

添加DBContext

经过上一步，我们已经创建了一个Employee模型，现在需要在这个模型类上添加另一个类，它将与实体框架(Entity Framework)进行通信来检索和保存数据。 以下是Employee.cs文件中的完整代码。

using System;
using System.Data.Entity;

namespace MVCScaffolding.Models{
   public class Employee{
      public int ID { get; set; }
      public string Name { get; set; }
      public DateTime JoiningDate { get; set; }
      public int Age { get; set; }
   }

   public class EmpDBContext : DbContext{
      public DbSet<Employee> Employees { get; set; }
   }
}

如上面所看到的EmpDBContext是从一个名为DbContext的EF类派生的。 在这个类中，有一个名为DbSet的属性，它基本上代表了要查询和保存的实体。

添加脚手架项目

要添加脚手架，请右键单击解决方案管理器中的Controllers文件夹，然后选择:添加 -> 新建搭建基架项目 ，如下图所示 -

它将显示“添加脚手架”对话框 -

选择：包含视图MVC 5控制器(使用Entity Framework) 项，然后单击“添加” 按钮，这将显示添加控制器对话框。

从模型类 下拉列表中选择：Employee，并从数据上下文类 下拉列表中选择EmpDBContext。还将看到默认情况下选择了控制器名称。

单击“添加”按钮继续，在由Visual Studio使用脚手架创建的EmployeesController中看到以下代码。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using MVCScaffolding.Models;

namespace MVCScaffolding.Controllers
{
    public class EmployeesController : Controller
    {
        private EmpDBContext db = new EmpDBContext();

        // GET: Employees
        public ActionResult Index()
        {
            return View(db.Employees.ToList());
        }

        // GET: Employees/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                return HttpNotFound();
            }
            return View(employee);
        }

        // GET: Employees/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: Employees/Create
        // 为了防止“过多发布”攻击，请启用要绑定到的特定属性，有关
        // 详细信息，请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "ID,Name,JoiningDate,Age")] Employee employee)
        {
            if (ModelState.IsValid)
            {
                db.Employees.Add(employee);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(employee);
        }

        // GET: Employees/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                return HttpNotFound();
            }
            return View(employee);
        }

        // POST: Employees/Edit/5
        // 为了防止“过多发布”攻击，请启用要绑定到的特定属性，有关
        // 详细信息，请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "ID,Name,JoiningDate,Age")] Employee employee)
        {
            if (ModelState.IsValid)
            {
                db.Entry(employee).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(employee);
        }

        // GET: Employees/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Employee employee = db.Employees.Find(id);
            if (employee == null)
            {
                return HttpNotFound();
            }
            return View(employee);
        }

        // POST: Employees/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Employee employee = db.Employees.Find(id);
            db.Employees.Remove(employee);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

运行应用程序并使用浏览器打开URL： http://localhost:59083/Employees ，将看到以下输出结果 -

可以看到视图中并没有数据，因为我们还没有向 Visual Studio 创建的数据库添加任何记录。

下面点击添加(Create New)链接来一个记录，它将显示创建视图，填入相关信息 -

点击“创建”按钮，它将更新Index 视图，看到有一条记录。如下所示-

也可以看到新记录也被添加到数据库中。

通过上面所看实现步骤可以看到，我们已经通过使用脚手架实现了一个简单的例子，这是一个用来创建模型类，视图和动作方法的更容易方式。

ASP.Net MVC Bootstrap

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_bootstrap.html

在本章中，我们将看看现在包含在ASP.NET和MVC中的前端框架Bootstrap。这是一个流行的Web应用程序前端工具包，将帮助我们建立一个HTML，CSS和JavaScript的用户界面。

它最初是由Twitter的网站开发人员为个人使用而创建的，但是由于其灵活性和易用性，它现在是开源的，并且已经深受设计人员和开发人员的欢迎。

您可以使用Bootstrap创建一个从小型移动屏幕到大型桌应用的界面。在本章中，我们还将介绍Bootstrap如何与布局视图一起工作来构造应用程序的外观。

Bootstrap提供布局，按钮，表单，菜单，小部件，图片轮播，标签，徽章，排版以及各种功能所需的所有部分。由于Bootstrap都是HTML，CSS和JavaScript，所有开放的标准，你可以使用任何框架，包括ASP.NET MVC。当你开始一个新的MVC项目时，Bootstrap将会出现，在项目中可以找到Bootstrap.css 和Bootstrap.js 。

我们来创建一个新的ASP.NET Web应用程序。填写项目的名称：MVCBootstrap，然后单击确定 ，会看到下面的对话框。

在这个对话框中，如果选择了空的模板，会得到一个空的web应用程序，并且不会有Bootstrap ，不会有任何控制器或任何其他脚本文件。

现在选择MVC模板，然后单击确定 。 当Visual Studio创建这个解决方案时，它将下载并安装到项目中的一个包将成为Bootstrap NuGet包。可以通过到packages.config 进行验证，可以看到有Bootstrap版本3 的包，如下图所示 -

还可以看到包含不同 css 文件的 Content 文件夹。

运行这个应用程序，会看到下面的页面。

出现此页面时，看到的大部分布局和样式都是由 Bootstrap 应用的布局和样式。 它包括顶部的导航栏，链接以及广告ASP.NET的显示。它还包括所有这些部分，关于入门和获得更多的类库和虚拟主机。

如果只是稍微拉伸一下浏览器，那么这些浏览器实际上并排放置，这是Bootstrap 响应式设计功能的一部分。

如果查看Content文件夹，那么应该会看到有一个Bootstrap.css 文件。
NuGet软件包还提供了一个缩小版本的文件。 在Scripts目录下，应该有一个：Bootstrap.js 文件，这是Bootstrap的一些组件所必需的。

它确实依赖于jQuery，幸运的是jQuery也安装在这个项目中，并且有一个缩小版的Bootstrap JavaScript文件。

现在的问题是，所有这些在应用程序中添加了哪里？ 可能您期望它在布局模板中，位于View/Shared/_layout.cshtml 下的此项目的布局视图。

布局视图控制着用户界面的结构。 以下是_layout.cshtml文件中的完整代码。

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("应用程序名称", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("主页", "Index", "Home")</li>
                    <li>@Html.ActionLink("关于", "About", "Home")</li>
                    <li>@Html.ActionLink("联系方式", "Contact", "Home")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - 我的 ASP.NET 应用程序</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

在上面的代码中有两点需要注意。 首先在<title>之后，看到下面这行代码。

@Styles.Render("~/Content/css")

Styles.Render对于 Content/css 实际上是Bootstrap.css文件将被包含的地方，在底部，会看到下面这行代码 -

@Scripts.Render("~/bundles/bootstrap")

它正在渲染引导脚本，找出这些包里面究竟是什么，打开App_Start文件夹中的BundleConfig.cs 文件，内容如下所示 -

在BundleConfig.cs 文件中，可以从底部看到CSS包包含了Bootstrap.css 和自定义的site.css 。如下所示 -

bundles.Add(new StyleBundle("~/Content/css").Include(
   "~/Content/bootstrap.css",
   "~/Content/site.css"));

这是我们添加自己的样式表来定制应用程序外观的地方。还可以看到在包含Bootstrap.js 的CSS包之前出现的Bootstrap包，以及另一个JavaScript文件respond.js。

bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
   "~/Scripts/bootstrap.js",
   "~/Scripts/respond.js"));

下面来注释掉Bootstrap.css 文件，如下面的代码所示 -

bundles.Add(new StyleBundle("~/Content/css").Include(
   //"~/Content/bootstrap.css",
   "~/Content/site.css"));

再次运行此应用程序，只是为了让您了解Bootstrap的功能，因为现在唯一可用的样式是site.css 文件中的样式。得到结果如下 -

正如上面所看到的，失去了页面顶部的导航栏。

现在来看看Bootstrap是什么。Bootstrap只是自动执行一些操作，当添加类并具有正确的HTML结构时，Bootstrap可为您做一些事情。 如果查看_layout.cshtml 文件，可以看到如以下代码所示的navbar类 -

<div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("应用程序名称", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("主页", "Index", "Home")</li>
                    <li>@Html.ActionLink("关于", "About", "Home")</li>
                    <li>@Html.ActionLink("联系方式", "Contact", "Home")</li>
                </ul>
            </div>
        </div>
    </div>

它是Bootstrap的CSS类，如navbar，navbar inverse和navbar fixed top。 如果删除了一些像navbar inverse，navbar fixed top这样的类，并且取消注释Bootstrap.css，然后再次运行应用程序，应该会看到下面的输出。

会看到有一个导航栏，但现在它不使用反色，所以它是白色的。 它也不会粘在页面的顶部。 当向下滚动时，导航栏会从顶部滚动，将无法再看到它。

ASP.Net MVC单元测试

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_unit_testing.html

在计算机编程中，单元测试是一种软件测试方法，通过这种方法对各个源代码单元进行测试，以确定它们是否适合使用。 换句话说，这是一个软件开发过程，在这个过程中，应用程序中最小的可测试部分(称为单元)被单独和独立地审查以便正确执行和操作。

在程序编程中，一个单元可以是一个完整的模块，但更常见的是一个单独的功能或过程。 在面向对象编程中，一个单元通常是一个完整的接口，比如一个类，但可能是一个单独的方法。

单元测试通常是自动的，但也可以手动完成。

单元测试的目标

单元测试的主要目标是在应用程序中使用最小的可测试软件，并确定其行为是否与期望的完全一致。每个单元在将它们集成到模块之前分别进行测试，以测试模块之间的接口。

我们来看一个单元测试的简单例子，在这个例子中使用单元测试选项来创建一个新的ASP.NET MVC应用程序。

第1步 - 打开Visual Studio，然后单击：文件 -> 新建 -> 项目 菜单选项。一个新的项目对话框打开。
第2步 - 在左侧窗格中，选择：模板 -> Visual C# -> Web 。
第3步 - 在中间窗格中，选择：ASP.NET Web应用程序。

第4步 - 在名称字段中输入项目名称为：MVCUnitTesting，然后单击确定 继续。
然后将看到下面的对话框，要求设置ASP.NET项目的初始内容。

第5步 - 选择MVC作为模板，不要忘记选中对话框底部的添加单元测试复选框。也可以更改测试项目名称，但在此示例中，我们将其保持原样，因为它是默认名称。

项目由Visual Studio创建后，将在“解决方案资源管理器”窗口中看到许多文件和文件夹。

第6步 - 可以看到在解决方案资源管理器中有两个项目。 一个是ASP.NET Web项目，另一个是单元测试项目。

第7步 - 运行这个应用程序，会看到下面的输出。

如上图所示，导航栏上有“首页”，“关于”和“联系人”按钮。这里点击“关于”链接，会看到下面的视图。

现在展开MVCUnitTestingDemo 项目，将看到 Controllers 文件夹下的HomeController.cs 文件。

HomeController 包含三个操作方法，如下面的代码所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVCUnitTesting.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}

展开MVCUnitTestingDemo.Tests 项目，Controllers文件夹下的HomeControllerTest.cs文件。

在这个HomeControllerTest类中有三个方法，如下面的代码所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MVCUnitTesting;
using MVCUnitTesting.Controllers;

namespace MVCUnitTesting.Tests.Controllers
{
    [TestClass]
    public class HomeControllerTest
    {
        [TestMethod]
        public void Index()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }

        [TestMethod]
        public void About()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.About() as ViewResult;

            // Assert
            Assert.AreEqual("Your application description page.", result.ViewBag.Message);
        }

        [TestMethod]
        public void Contact()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.Contact() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }
    }
}

这三个方法将测试Index，About和Contact操作方法是否正常工作。要测试这三个操作方法，请转到测试 菜单。选择：运行 -> 所有测试 项来测试这些操作方法。

现在会看到左边的测试资源管理器，可以看到所有的测试都通过了。再添加一个动作方法，它用于列出所有的员工。首先，需要在Models 文件夹中添加一个Employee类。
以下是Employee类的实现 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace MVCUnitTesting.Models
{
    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime JoiningDate { get; set; }
        public int Age { get; set; }
    }
}

添加EmployeeController 。 右键单击解决方案资源管理器 中的Controllers 文件夹，然后选择：添加 -> 控制器 ，它将显示“添加基架”对话框。选择：MVC 5控制器 - 空 选项，然后点击“添加” 按钮，如下图所示 -

添加控制器对话框将出现。将名称设置为：EmployeeController，然后单击“添加”按钮。

在Controllers 文件夹中看到一个新的 C# 文件 - EmployeeController.cs，该文件夹在Visual Studio中打开并进行编辑。这里使用下面的代码更新 EmployeeController 。

using MVCUnitTesting.Models;
using System;
using System.Collections.Generic;
using System.Linq;

using System.Web;
using System.Web.Mvc;

namespace MVCUnitTesting.Controllers
{
    public class EmployeeController : Controller
    {
        [NonAction]

        public List<Employee> GetEmployeeList()
        {
            return new List<Employee>{
            new Employee{
               ID = 1,
               Name = "Maxsu",
               JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
               Age = 23
            },

            new Employee{
               ID = 2,
               Name = "Carson",
               JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
               Age = 45
            },

            new Employee{
               ID = 3,
               Name = "Kobe Bryant",
               JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
               Age = 37
            },

            new Employee{
               ID = 4,
               Name = "Laura",
               JoiningDate = DateTime.Parse(DateTime.Today.ToString()),
               Age = 26
            },
         };
        }

        // GET: Employee
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Employees()
        {
            var employees = from e in GetEmployeeList()
                            orderby e.ID
                            select e;
            return View(employees);
        }
    }
}

要为Employee操作方法添加视图，请右键单击Employees方法并选择：添加视图…

您将看到视图的默认名称。从“模板”下拉列表中选择“List”，从“模型类”下拉列表中选择“Employee”，然后单击“确定”。

现在需要添加一个链接到Employees列表，打开Views/Shared 文件夹下的_layout.cshtml 文件，并在联系人 链接下面添加员工列表 链接。

<li>@Html.ActionLink("员工列表", "Employees", "Employee")</li>

以下是_layout.cshtml 的完整实现 -

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - 我的 ASP.NET 应用程序</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("应用程序名称", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("主页", "Index", "Home")</li>
                    <li>@Html.ActionLink("关于", "About", "Home")</li>
                    <li>@Html.ActionLink("联系方式", "Contact", "Home")</li>
                    <li>@Html.ActionLink("员工列表", "Employees", "Employee")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - 我的 ASP.NET 应用程序</p>
        </footer>
    </div>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>

要从Employee控制器测试Employees动作方法，需要在单元测试项目中添加另一个测试方法。在HomeControllerTest.cs文件中的EmployeeControllerTest类代码之后，如下所示 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MVCUnitTesting;
using MVCUnitTesting.Controllers;

namespace MVCUnitTesting.Tests.Controllers
{
    [TestClass]
    public class HomeControllerTest
    {
        [TestMethod]
        public void Index()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }

        [TestMethod]
        public void About()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.About() as ViewResult;

            // Assert
            Assert.AreEqual("Your application description page.", result.ViewBag.Message);
        }

        [TestMethod]
        public void Contact()
        {
            // Arrange
            HomeController controller = new HomeController();

            // Act
            ViewResult result = controller.Contact() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }
    }

    [TestClass]
    public class EmployeeControllerTest
    {
        [TestMethod]
        public void Employees()
        {
            // Arrange
            EmployeeController controller = new EmployeeController();

            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }
    }
}

从测试 菜单中选择：运行 -> 所有测试 项来测试这些操作方法。

可以看到Employees测试方法现在也通过了。运行应用程序时将看到以下输出。

点击导航栏中的“员工列表”选项，将看到员工列表信息，如下图所示 -

ASP.Net MVC自托管（本地主机部署）

原文：https://www.yiibai.com/asp.net_mvc/asp.net_mvc_selfhosting.html

在这一章中，我们将介绍自托管。 自托管为应用程序创建一个运行环境，可以在任何环境下运行，例如：在MAC或Linux机器中运行。自托管也意味着它将拥有一个微型CLR版本。

使用文件系统进行部署

我们来看一个简单的自托管的例子。

第1步 - 当ASP.NET MVC应用程序完成之后，假设想要使用自托管部署应用，右键单击解决方案资源管理器 中的项目，然后选择：发布 ，弹出下面的对话框。

第2步 - 从发布方法下拉列表中选择文件系统(FolderProfile)，并点击设置…目标位置为：F:\worksp\asp.net_mvc\selfhosting 。 点击“下一步”按钮。

第3步 - 展开文件发布 选项。选中“发布前删除所有现有文件” 和“发布时预先编译” 复选框，然后单击“保存”继续。

第4步 - 回到主页面，点击发布，如下所示 -

发布成功结果如下所示 -

打开系统中目标位置的所有文件和文件夹(F:\worksp\asp.net_mvc\selfhosting)，如下图所示 -

它将具有在本地主机上部署所需的所有文件。

第5步 - 现在打开：控制面板 -> 程序和功能，并展开：Internet信息服务 -> 万维网服务 -> 应用程序开发功能 。

注：这里为了方便学习，几乎所有功能都选择了。

第6步 - 选中复选框，如上面的屏幕截图所示，然后点击确定。配置过程需要一定的时间，请耐心等待直至完成。
第7步 - 点击“开始”-“控制面板”，找到”系统和安全”->”管理工具”，并双击打开IIS管理器，如以下屏幕截图所示。

第7步 - 在屏幕左侧看到不同的连接，右键单击：网站 ，然后选择添加网站，如下所示 -

如上图中所示，它的物理路径和在发布时使用文件系统时所提到的是一样的(F:\worksp\asp.net_mvc\selfhosting)。

第8步 - 点击确定 继续。启动网站，打开浏览器，访问：localhost/FirstWebApp，应该会看到与之前显示相同的结果。