Asp.Net MVC(创建一个任务列表应用程序) - Part.1

原文链接:http://www.asp.net/learn/mvc/tutorial-01-cs.aspx

这篇教程的目的是让你了解如何创建一个Asp.Net MVC应用程序。在教程中,我从头至尾快速地创建了一个完整的Asp.Net应用程序。我向你演示了如何创建一个简单的任务列表(Tasklist)应用程序。

如果你曾经使用过动态服务器页(ASP)或者ASP.NET,那么你应该会发现ASP.NET MVC非常熟悉。ASP.NET MVC的视图非常类似于ASP中的页面。并且,正如一个传统的ASP.NET Web窗体应用程序,ASP.NET MVC为你提供了对.NET框架所提供的丰富的语言和类集的完全访问。

我希望这篇教程将会给你这样一种感觉:创建一个ASP.NET MVC应用程序的过程与创建一个ASP或者ASP.NET Web窗体应用程序既相似又有所区别。

1. 任务列表应用程序

简单起见,我们将会创建一个非常简单的任务列表(TaskList)应用程序。我们的这个简单的任务列表应用程序将会允许我们做下面三件事:

  1. 列出任务集合
  2. 创建新任务
  3. 将一个任务标识为已完成

同样,简单起见,我们使用了创建应用程序所需的ASP.NET MVC框架的最少数量的功能。举个例子,我们不会利用测试驱动开发或者HTML帮助方法。

1.1 准备工作

为了创建一个ASP.NET MVC应用程序,你需要Visual Studio 2008或者是Visual Web Developer 2008 Express。你也需要下载ASP.NET MVC框架。如果你没有自己的Visual Studio 2008,那么你可以从这个站点下载一个Visual Studio 2008的90天试用版本:

http://msdn.microsoft.com/en-us/vs2008/products/cc268305.aspx

除此以外,你可以使用Visual Web Developer Express 2008创建ASP.NET MVC应用程序。如果你选择使用 Visual Web Developer Express,那么你必须安装Service Pack 1。你可以从下面的站点下载到Visual Web Developer Express 2008和Service Pack 1。

http://www.microsoft.com/downloads/details.aspx?FamilyId=BDB6391C-05CA-4036-9154-6DF4F6DEBD14&displaylang=en

在你安装好Visual Studio 2008或者Visual Web Developer 2008之后,你需要安装ASP.NET MVC框架。你可以从下面的站点下载到ASP.NET MVC框架:

http://www.asp.net/mvc/

1.2 创建一个ASP.NET MVC Web应用程序项目

让我们在Visual Studio 2008中创建一个新的ASP.NET MVC Web应用程序项目作为开始。选择菜单选项,“文件(File)”、“新建项目(New Project)”,然后你将会看到新建项目对话框,如图1所示。选择你最喜欢的编程语言(Visual Basic或者Visual C#),并且选择ASP.NET MVC Web应用程序项目。将项目命名为TaskList,然后点击“确定(OK)”按钮。

图1. 新建项目对话框

不论何时创建一个MVC Web应用程序项目,Visual Studio都会询问你是否创建一个单独的单元测试项目。出现图2所示的对话框。因为时间限制,所以我们不想在这篇教程中创建测试项目(是的,我们应该为此感到一丝愧疚),选择“否(NO)”选项,然后点击“确定(OK)”按钮。

图2.创建单元测试项目的对话框

一个ASP.NET MVC应用程序拥有一系列标准的文件夹:一个Models、Views和Controllers文件夹。你可以在解决方案浏览器窗口中看到这些标准的文件夹。我们将需要向Models、Views和Controllers中添加文件来创建我们的TaskList应用程序。

当你使用Visual Studio创建一个新的MVC应用程序,将会获得一个范例应用程序。因为我们想从零做起,所以需要删除这个范例应用程序的内容。你需要删除下面的文件和文件夹:

  • Controllers\HomeController.cs
  • Views\Home

1.3 创建控制器

典型地,当我们创建一个ASP.NET MVC应用程序时,你将以创建一个控制器(Controller)作为开始。每一个对ASP.NET MVC应用程序的请求对会由一个控制器处理。一个控制器包含了响应一个请求的应用程序逻辑。

通过右键点击控制器文件夹,并且在菜单项中选择“添加(Add)”、“新建项(New Item)” ,向你的Visual Studio项目中添加一个新的控制器。选择“MVC Controller Class”模板。将你的新控制器命名为HomeController.cs并且点击“添加(Add)”按钮。

对于我们的任务列表应用程序,我们修改HomeController类,使它包含清单1中的代码。修改后的控制器含有四个方法,名为Index()、Create()、CreateNew()和Complete()。每一个方法对应于一个控制器的动作。

代码清单1 – HomeController.cs

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

namespace TaskList.Controllers {
    public class HomeController : Controller {

        // 显示任务列表
        public ActionResult Index() {
            return View();
        }

        // 显示一个表单用于创建新任务
        public ActionResult Create() {
            return View();
        }

        // 向数据库中添加一个新任务
        public ActionResult CreateNew() {
            return RedirectToAction("Index");
        }

        // 将一个任务标识为已完成
        public ActionResult Complete() {
            // CODE:数据库逻辑
            return RedirectToAction("Index");
        }
    }
}

这里是这些控制器动作背后的意图:

  • Index() – 当你想要显示一个任务列表时被调用。
  • Create() – 当你想要显示一个用于添加新任务的表单时被调用。
  • CreateNew() – 当添加新任务的表单被提交时调用。这个控制器动作实际地将新任务添加到数据库中。
  • Complete() – 当新任务被标识为完成时调用。

我们需要添加额外的逻辑到我们的控制器动作中,以使它们能够按照预期工作。

包含在控制器类中的任何公共方法,都被暴露为控制器动作。注意这里。一个控制器动作被暴露给了全世界。任何人都可以通过在Web浏览器的地址栏中输入正确的URL来调用控制器动作。因此,在你并不需要方法被调用时,不要随意地在控制器中创建公共方法。

注意到控制器动作返回一个ActionResult。ActionResult代表着动作将要完成的事情。前两个控制器动作,Index()和Create(),返回了一个MVC视图。第三个和第四个动作的结果是重定向用户到另一个控制器动作。

这便是这些控制器动作如何工作了。当你请求Create()控制器动作时,返回了一个视图,该视图含有用于创建新任务的表单。当你提交这个表单,将会调用CreateNew()控制器动作。CreateNew控制器动作将新任务添加到了数据库中,并且将用户重定向到了Index()控制器动作。Index()控制器动作返回了一个视图,这个视图显示了完整的任务列表。最后,如果你将一个任务标识为已完成,会调用Compete()控制器动作,并更新数据库。Complete()控制器将用户重定向回Index()动作,并且显示已更新的任务列表。

1.4 创建视图

视图含有发往浏览器的HTML标记和内容。在ASP.NET MVC应用程序中视图与页面最为相似。你可以通过创建一个后缀名为.aspx的文件来创建一个视图。

你必须将视图放置在正确的位置。如果你正在为HomeController的Index()动作方法创建视图,那么你必须将视图放置在符合如下路径的文件夹下:

\Views\Home\Index.aspx

如果你正在ProductController的Price()动作方法创建一个视图,那么视图必须被放置在下面的文件夹中:

\Views\Product\Price.aspx

默认情况下,视图应该和与其相应的控制器动作拥有相同的名称。视图同样应该被置于一个文件夹中,该文件夹与控制器的名称相对应。

通过在Views文件夹下的子文件夹上点击右键,并且选择菜单项“添加(Add)”,“新建项(New Item)”,可以创建一个视图。选择“MVC 视图页面(MVC View Page)” 模板来添加一个新的视图。我们需要按下面的路径创建两个视图:

\Views\Home\Index.aspx

\Views\Home\Create.aspx

在你创建好这两个视图以后,你的解决方案浏览器窗口应该含有如图3所示的文件:

图3. Index.aspx和Create.aspx视图

一个视图可以包含HTML内容和脚本。Index.aspx视图将会被用于显示所有任务的列表。为了说明这个视图的目的,在Index.aspx视图中添加清单2中的代码。

代码清单2 – Index.aspx

<%@ Page Language="C#" AutoEventWireup="false" CodeBehind="Index.aspx.cs" Inherits="TaskList.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
     <head runat="server">
          <title></title>
     </head>
     <body>
          <div>
               <h1>My Tasks</h1>
               ... displaying all tasks
               <a href="/Home/Create">Add new Task</a>
          </div>
     </body>
</html>

现在Index.aspx视图并没有显示任何的任务 – 它只是声明它将会显示。在这个教程后面,我们将会为Index.aspx页面添加一些脚本来显示任务列表。

注意到Index.aspx视图含有标为“Add new task”的链接。这个链接指向到了/Home/Create路径。当你点击这个链接,HomeController的Create()动作将会被调用。Create()方法返回Create视图。

Create.aspx视图含有一个用于创建新任务的表单。这个视图的内容包含在清单3中:

清单3 - Create.aspx

<%@ Page Language="C#" AutoEventWireup="false" CodeBehind="Create.aspx.cs" Inherits="TaskList.Views.Home.Create" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
     <head runat="server">
          <title></title>
     </head>
     <body>
          <div>
               <h1>Add New Task</h1>
               <form method="post" action="/Home/CreateNew">
                    <label for="description">Task:</label>
                    <input type="text" name="description" />
                    <br />
                    <input type="submit" value="Add Task" />
               </form>
          </div>
     </body>
</html>

注意到清单3中包含的表单提交到了下面的URL:

/Home/CreateNew.aspx

这个URL对应于HomeController控制器中的CreateNew()动作。代表着新任务的表单数据将会提交到这个动作。

1.5 创建数据库

下一步是创建含有任务的数据库。你可以通过在App_Data文件夹上点击右键,并且选择菜单项,“添加(Add)”、“新建项(New Item)”来创建数据库。选择“SQL Server数据库”模板项,将数据库命名为TaskListDB.mdf,并且点击“添加(Add)”按钮。

接下来,我们需要向数据库中添加一张含有任务的表。在解决方案浏览器窗口中双击TaskListDB.mdf以打开服务器浏览器窗口。右键点击“表”文件夹,并且选择“添加新表(Add New Table)”菜单项。选择这个菜单项将会打开数据库表设计器。创建下面的数据库列:

列名 数据类型 是否允许NULL
Id Int False
Task NVarchar(300) False
IsCompleted Bit False
EntryDate DateTime False

第一列,Id列,有两个特殊的属性。首先,你需要将Id标记为主键列。在选择Id列之后,点击“设置主键(Set Primary Key)”按钮(这是一个看上去像钥匙的图标)。第二步,你需要将Id列标记为标识列(Identity Column)。在列属性窗口,向下滚动到“标识详细信息(Identity Specification)”一节并展开它。将“是否为标识(Is Identity)”值改为Yes。当你做完这些以后,表格将会如图4所示。

图4. Tasks表格

最后一步是保存这个新的表格。点击“保存(Save)”按钮(软盘的图标)并且将新表格命名为Tasks。

1.6 创建模型

一个MVC模型包含了你的应用程序和数据库访问的大部分逻辑。通常,你可以将包含在MVC应用程序中的大部分类都放在Models文件夹中。你的应用程序中所有没有包含在视图或者控制器的逻辑都被推向了Models文件夹。

在这篇教程中,我们将会使用LINQ to SQL来与我们上一小节创建的数据库通信。就我自己而言,我喜欢LINQ to SQL。然而,并不要求你一定在ASP.NET MVC应用程序中使用LINQ to SQL。如果你愿意,你可以使用其他的技术,例如NHibernate或者Entity Framework来与数据库进行通信。

为了使用LINQ to SQL,我们必须首先在Models文件夹中创建我们的LINQ to SQL类。右键点击Models文件夹,选择“添加”、“新建项”菜单项,并且选择“LINQ to SQL Classes”模板项。将你新建的LINQ to SQL类命名为TaskList.dbml并且点击“添加(Add)”按钮。在你完成这一步骤之后,将会显示对象关系设计器(Object Relational Designer)。

我们需要创建一个LINQ to SQL实体类,它代表了我们的Tasks数据库表。将Tasks数据库表从解决方案浏览器窗口拖曳到对象关系设计器中。执行完最后这一个动作,将会创建一个新的名为Task的LINQ to SQL实体类(参看图5)。点击“保存(Save)”按钮(软盘的图标)以保存这个新的实体。

图5 - Task实体

1.7 向控制器方法中添加数据库逻辑

现在我们已经拥有了数据库,我们可以修改我们的控制器动作,以便从数据库中存储和获取任务。修改后的HomeController包含在清单4中。

清单4 - HomeController.cs

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

namespace TaskList.Controllers {
    public class HomeController : Controller {
        private TaskListDataContext db = new TaskListDataContext();
        // 显示任务列表
        public ActionResult Index() {
            var tasks = from t in db.Tasks
                            orderby t.EntryDate
                            descending select t;
            return View(tasks.ToList());
        }

        // 显示创建新任务的表单
        public ActionResult Create() {
            return View();
        }

        // 向数据库中添加一个新任务
        public ActionResult CreateNew(string description) {
            Task newTask = new Task();
            newTask.Description = description;
            newTask.IsCompleted = false;
            newTask.EntryDate = DateTime.Now;

            db.Tasks.InsertOnSubmit(newTask);
            db.SubmitChanges();

            return RedirectToAction("Index");
        }

        // 将任务标记为任务
        public ActionResult Complete(int Id) {
            // 数据库逻辑
            var tasks = from t in db.Tasks where t.Id == Id select t;
            foreach (Task match in tasks)
                match.IsCompleted = true;

            db.SubmitChanges();

            return RedirectToAction("Index");
        }
    }
}

注意到清单4中的HomeController含有一个类级(class-level)的私有字段,名为db。这个db字段是一个TaskListDataContext类的实例。HomeController类使用db字段来代表TaskListDB数据库。

Index()控制器的动作已经被修改为获得所有来自Tasks数据库表的记录。tasks被传递到了Index视图中。

CreateNew()方法被修改为在Tasks数据库表中创建一个新的任务。注意到CreateNew()方法已经被修改为接受一个名为description的字符串参数。这个参数代表着由Create视图传递进来的description文本表单字段。ASP.NET MVC框架自动将表单字段作为参数传递给控制器动作。

最后,Complete()方法被修改为更改Tasks数据库表的IsComplete列的值。当你将一个任务标记为已完成,任务的Id被传递给Complete()动作,并更新数据库。

1.7 修改Index视图

为了完成我们的TaskList应用程序,我们还必须再做最后一件事。我们必须修改Index视图,以便它能够显示所有任务的列表,并且允许我们将某一任务标记为已完成。修改过的Index视图包含在了清单5中。

清单5 - Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="TaskList.Views.Home.Index" %>
<%@ Import Namespace="TaskList.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
     <head runat="server">
          <title>Index</title>
     </head>
     <body>
          <div>
               <h1>My Tasks</h1>
               <ul>
                    <% foreach (Task task in (IEnumerable)ViewData.Model) { %>
                         <li>
                              <% if (task.IsCompleted) {%>
                                   <del>
                                        <%= task.EntryDate.ToShortDateString() %>
                                        -- <%=task.Description%>
                                   </del>
                              <% } else {%>
                                   <a href="/Home/Complete/<%= task.Id.ToString() %>">Complete</a>
                                   <%= task.EntryDate.ToShortDateString() %>
                                   -- <%=task.Description%>
                              <% }%>
                         </li>
                    <% } %>
               </ul>
               <br /><br />
               <a href="/Home/Create">Add new Task</a>
          </div>
     </body>
</html>

在清单5中的Index视图含有一个C# foreach循环,它遍历了所有的任务。这些任务由ViewData.Model属性所表示。概括来讲,你使用ViewData来将数据从控制器传递到视图。

在循环体内部,有一个条件判断用来检查任务是否已完成。一个已完成的任务使用一个穿过它的横线(删除线)来表示。HTML的<del>标签用来创建横穿已完成任务的横线。如果这个任务还没有完成,将在任务旁边显示一个标记为Complete的链接。这个链接由下面的脚本创建:

<a href="/Home/Complete/<%= task.Id.ToString() %>">Complete</a>

注意到任务的Id被包含在由链接所表示的URL中。当你点击链接时,这个任务Id将会传递到HomeController类的Complete()动作中。通过这种方式,当你点击Complete链接时,将会更新正确的数据库记录。

Index视图的最终版本所显示的页面位于图6中。

图6 - Index视图

1.8 总结

这篇教程的目的是让你体验一下创建一个ASP.NET MVC应用程序的经过。我希望你会发现创建一个ASP.NET MVC Web应用程序与创建一个动态服务器页面(ASP)或者ASP.NET应用程序的经历非常类似。

在这篇教程中,我们只考察了ASP.NET MVC框架中最基本的一些功能。在后续的教程中,我们将会更加深入到一些主题,例如控制器、控制器动作、视图、视图数据和HTML helper中。