supegong  

新建项目
      安装完ASP.NET MVC后,在VS里新建一个项目,可以看到有一个“ASP.NET MVC Web Application”的选项,选择它,新建一个项目,并命名为“MVCDemo”。
      建完项目后,可以看到默认情况下已经建立了很多文件夹,这里简略说一下各个文件夹的作用。
      Content——存放应用需要的一些资源文件,如图片、CSS等。
      Controllers——存放控制器类。
      Models——存放业务模型组件。
      Scripts——存放JavaScript脚本文件。
      Views——存放视图。
      现在不了解一些概念没关系,后续文章将慢慢解释。

准备工作
      新建完项目后,我们要做的准备工作就是建立Mock业务模型,这样以后我们就直接使用这些“欺骗”式的业务模型进行业务处理,而将全部关注点放在ASP.NET MVC的学习上。
      首先在Models下新建三个文件夹,分别叫做Entities、Interfaces、MockModels,分别用来存放实体类、接口及Mock业务模型。
      Entities下有两个类:CategoryInfo和AnnounceInfo,分别是公告类别和公告的实体类。具体代码如下:
CategoryInfo.cs:

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

namespace MVCDemo.Models.Entities
{
    
/// <summary>
    
/// 分类实体类
    
/// </summary>
    public class CategoryInfo
    {
        
public int ID { getset; }
        
public string Name { getset; }
    }
}


AnnounceInfo.cs:

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

namespace MVCDemo.Models.Entities
{
    
/// <summary>
    
/// 公告实体类
    
/// </summary>
    public class AnnounceInfo
    {
        
public int ID { getset; }
        
public string Title { getset; }
        
public string Content { getset; }
        
public int Category { getset; }
    }
}


      接下来,我们将定义两个接口,分别是公告类别服务和公告服务必须实现的接口。这两个接口放在Interfaces下。
ICategoryService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MVCDemo.Models.Entities;

namespace MVCDemo.Models.Interfaces
{
    
/// <summary>
    
/// 分类服务组件接口
    
/// </summary>
    public interface ICategoryService
    {
        
/// <summary>
        
/// 添加分类
        
/// </summary>
        
/// <param name="category"></param>
        void Add(CategoryInfo category);

        
/// <summary>
        
/// 修改分类名称
        
/// </summary>
        
/// <param name="id"></param>
        
/// <param name="name"></param>
        void ChangeName(int id,string name);

        
/// <summary>
        
/// 删除分类
        
/// </summary>
        
/// <param name="id"></param>
        void Remove(int id);

        
/// <summary>
        
/// 取得某个分类详细信息
        
/// </summary>
        
/// <param name="id"></param>
        
/// <returns></returns>
        CategoryInfo GetDetail(int id);

        
/// <summary>
        
/// 取得所有分类
        
/// </summary>
        
/// <returns></returns>
        List<CategoryInfo> GetAll();
    }
}


IAnnounceService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MVCDemo.Models.Entities;

namespace MVCDemo.Models.Interfaces
{
    
/// <summary>
    
/// 公告服务组件接口
    
/// </summary>
    public interface IAnnounceService
    {
        
/// <summary>
        
/// 发布公告
        
/// </summary>
        
/// <param name="announce"></param>
        void Release(AnnounceInfo announce);

        
/// <summary>
        
/// 修改公告信息
        
/// </summary>
        
/// <param name="announce"></param>
        void Notify(AnnounceInfo announce);

        
/// <summary>
        
/// 删除公告
        
/// </summary>
        
/// <param name="id"></param>
        void Remove(int id);

        
/// <summary>
        
/// 取得公告详细内容
        
/// </summary>
        
/// <param name="id"></param>
        
/// <returns></returns>
        AnnounceInfo GetDetail(int id);

        
/// <summary>
        
/// 取得某个分类下的所有公告
        
/// </summary>
        
/// <param name="categoryId"></param>
        
/// <returns></returns>
        List<AnnounceInfo> GetByCategory(CategoryInfo category);
    }
}


然后,我们在MockModels下建立两个Mock业务逻辑服务模型。注意它们要各自实现自己的接口。
MockCategoryService.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCDemo.Models.Interfaces;
using MVCDemo.Models.Entities;

namespace MVCDemo.Models.MockModels
{
    
/// <summary>
    
/// “欺骗”服务组件,用于模拟分类的业务服务
    
/// </summary>
    public class MockCategoryService : ICategoryService
    {
        
/// <summary>
        
/// 添加分类
        
/// </summary>
        
/// <param name="category"></param>
        public void Add(CategoryInfo category)
        {
            
return;
        }

        
/// <summary>
        
/// 修改分类名称
        
/// </summary>
        
/// <param name="id"></param>
        
/// <param name="name"></param>
        public void ChangeName(int id, string name)
        {
            
return;
        }

        
/// <summary>
        
/// 删除分类
        
/// </summary>
        
/// <param name="id"></param>
        public void Remove(int id)
        {
            
return;
        }

        
/// <summary>
        
/// 取得某个分类详细信息
        
/// </summary>
        
/// <param name="id"></param>
        
/// <returns></returns>
        public CategoryInfo GetDetail(int id)
        {
            
return new CategoryInfo
            {
                ID 
= id,
                Name 
= "最新通告",
            };
        }

        
/// <summary>
        
/// 取得所有分类
        
/// </summary>
        
/// <returns></returns>
        public List<CategoryInfo> GetAll()
        {
            List
<CategoryInfo> categories = new List<CategoryInfo>();
            
for (int i = 1; i <= 5; i++)
            {
                CategoryInfo category 
= new CategoryInfo
                {
                    ID 
= i,
                    Name
="通告类别"+i,
                };

                categories.Add(category);
            }

            
return categories;
        }
    }
}


MockAnnounceService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCDemo.Models.Interfaces;
using MVCDemo.Models.Entities;

namespace MVCDemo.Models.MockModels
{
    
/// <summary>
    
/// “欺骗”服务组件,用于模拟公告的业务服务
    
/// </summary>
    public class MockAnnounceService : IAnnounceService
    {
        
/// <summary>
        
/// 发布公告
        
/// </summary>
        
/// <param name="announce"></param>
        public void Release(AnnounceInfo announce)
        {
            
throw new Exception("发布公告失败了!原因?没有原因!我是业务组件,我说失败就失败!");
            
return;
        }

        
/// <summary>
        
/// 修改公告信息
        
/// </summary>
        
/// <param name="announce"></param>
        public void Notify(AnnounceInfo announce)
        {
            
return;
        }

        
/// <summary>
        
/// 删除公告
        
/// </summary>
        
/// <param name="id"></param>
        public void Remove(int id)
        {
            
return;
        }

        
/// <summary>
        
/// 取得公告详细内容
        
/// </summary>
        
/// <param name="id"></param>
        
/// <returns></returns>
        public AnnounceInfo GetDetail(int id)
        {
            
return new AnnounceInfo
            {
                ID 
= id,
                Title 
= "" + id + "则公告",
                Content 
= "全体同学明早九点集体做俯卧撑!",
            };
        }

        
/// <summary>
        
/// 取得某个分类下的所有公告
        
/// </summary>
        
/// <param name="categoryId"></param>
        
/// <returns></returns>
        public List<AnnounceInfo> GetByCategory(CategoryInfo category)
        {
            List
<AnnounceInfo> announces = new List<AnnounceInfo>();
            
for (int i = 1; i <= 10; i++)
            {
                AnnounceInfo announce 
= new AnnounceInfo
                {
                    ID 
= i,
                    Title 
= category.Name+ "的第" + i + "则公告",
                    Content 
= "全体同学明早九点集体做俯卧撑!",
                };

                announces.Add(announce);
            }

            
return announces;
        }
    }
}


      可以看到,这两个类并没有访问数据库,也没有实现真正的业务逻辑,而不过是“捏造”了一些数据骗骗我们的表示层而已。
      最后,我们要建立一个生成业务逻辑模型的生成器,用来实现表示层和业务逻辑层的解耦。当然,为了简化复杂度,这里没有使用依赖注入机制。下面是我们生成器的代码,这个类是直接放在Models下的。
ServiceBuilder.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MVCDemo.Models.Interfaces;
using MVCDemo.Models.MockModels;

namespace MVCDemo.Models
{
    
/// <summary>
    
/// 服务组件生成类,用于生成业务服务组件
    
/// </summary>
    public sealed class ServiceBuilder
    {
        
/// <summary>
        
/// 创建分类服务组件
        
/// </summary>
        
/// <returns>分类服务组件</returns>
        public static ICategoryService BuildCategoryService()
        {
            
return new MockCategoryService();
        }

        
/// <summary>
        
/// 创建公告服务组件
        
/// </summary>
        
/// <returns>公告服务组件</returns>
        public static IAnnounceService BuildAnnounceService()
        {
            
return new MockAnnounceService();
        }
    }
}


      OK,到这里,我们的准备工作就做完了。完成这些后,系统的目录结构如下图所示:

 
小结
      在这篇文章中,我们只是讲了一下这个系列文章要做什么,以及为案例做了一些准备工作。从下篇开始,我们将正式开始使用ASP.NET MVC完成这个案例。
让第一个页面跑起来
      现在,我们来实现公告系统中的第一个页面——首页。它非常简单,只包括所有公告分类的列表,并且每个列表项是一个超链接。其中分类数据是用我们的Mock组件得到的。实现后界面如下:

 

      在开始之前,我们要删几个东西。因为默认情况下建立一个MVC项目时里面包含了几个示例页面,我们要做的就是:
      1.将Controllers文件夹下所有文件删除。
      2.将Views文件夹下除了Shared文件夹和Web.config外的所有文件删除,然后将Shared文件夹里面的文件删除。
      完成以上几步后,就可以开始实现第一个页面了。

实现控制器
      在Controllers文件夹下新建一个文件,类型选择“MVC Controller Class”,名字命名为HomeController.cs。这就是一个控制器类。然后我们为它编码,具体代码如下:
HomeController.cs:

 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Web;
 5using System.Web.Mvc;
 6using System.Web.Mvc.Ajax;
 7using MVCDemo.Models;
 8using MVCDemo.Models.Interfaces;
 9using MVCDemo.Models.Entities;
10
11namespace MVCDemo.Controllers
12{
13    public class HomeController : Controller
14    {
15        public ActionResult Index()
16        {
17            ICategoryService cServ = ServiceBuilder.BuildCategoryService();
18            ViewData["Categories"= cServ.GetAll();
19            return View("Index");
20        }

21    }

22}


      直观看来,这个类不是很复杂。它首先继承了Controller类。Controller类是ASP.NET MVC框架中提供的一个控制器积累,所有我们自定义的控制器类都要继承此基类。然后这个类中有一个Index方法,返回值类型是ActionResult。
      这里对其中涉及到的概念简单解释一下。首先,控制器类可以说是ASP.NET MVC的核心类,因为它将处理一切请求,并处理所有页面转发等表示逻辑,这也是使用了ASP.NET MVC后与传统ASP.NET应用最大的差别。在传统模式下,一个用户请求的url将对应一个aspx文件,而在ASP.NET MVC下,一个用户请求对应某个控制器类中的一个方法,而这个方法,就叫做一个Action。至于如何对应的,则是通过对url的解析。
      例如,在传统情况下,http://localhost/Default.aspx表示请求网站根目录下的Default.aspx文件。而现在,url可能变成了这种样子:http://localhost/Home/Index。这个意思就是,请求名叫HomeController控制器类下的Index方法。一般地,默认情况下,请求url的格式为http://localhost/{ControllerName}/{ActionName}。其中{ControllerName}是控制器类名“Controller”前的部分,{ActionName}就是方法名。
      当然,这种映射规则是可以更改的,而且请求Action时也可以传递参数,但这些都是后话,以后再慢慢讨论。

      下面再深入Index方法,看看这个Action都做了什么。它首先调用了业务逻辑组件(当然,是Mock的),然后将GetAll返回的公告分类数据赋予ViewData["Category"],最后调用View()方法返回一个ActionResult。ViewData是什么呢?你可以把他理解成一个关联数组,它保存需要传给视图的数据。而View是Controller类的一个方法,它返回一个ActionResult实例。这样说可能有点抽象,其实直观就是将某个视图(一般就是一个aspx文件)呈现到浏览器中。那么如何知道呈现哪一个视图呢?默认情况下,View方法会到网站的Views文件夹下的与控制器类同名的文件夹下寻找与Action方法同名的视图。例如,HomeController的Index方法就会寻找Views/Home/Index.aspx,如果找不到,就会到Shared下寻找,再找不到就报错了。当然,你也可以给View方法传递一个字符串参数,表示视图名称。

实现视图
      上文说到,当请求http://localhost/Home/Index时,HomeController的Index方法会被调用,而Index方法最后要呈现Views/Home/Index.aspx视图,所以,我们要在Views文件夹下建立一个Home文件夹,然后再新建一个Index.aspx视图。如果您使用的是VS2008 SP1,那么建立视图非常方便,只要在Home文件夹下右键单击,选Add--->View,然后指定视图名就可以了。如果不是SP1的,就新建一个Item,类型选择“MVC View Page”。建立好的视图其实就是一个aspx页面,但是其继承了View。这也是一个基类,所有视图需要继承它。
      下面给出Index.aspx的代码:
Index.aspx:

 1<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MVCDemo.Views.Home.Index" %>
 2<%@ Import Namespace="MVCDemo.Models.Entities" %>
 3
 4<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 5
 6<html xmlns="http://www.w3.org/1999/xhtml" >
 7<head runat="server">
 8    <title></title>
 9</head>
10<body>
11    <% List<CategoryInfo> categories=ViewData["Categories"as List<CategoryInfo>%>
12    <div>
13        <h1>MVC公告发布系统</h1>
14        <ul>
15            <% foreach (CategoryInfo c in categories)
16               {
17                    
%>
18            <li><%= Html.ActionLink(c.Name, "List/" + c.ID, "Announce"%></li>
19            <% } %>
20        </ul>
21    </div>
22</body>
23</html>


      大约分析一下。刚才说过,Index这个Action最后呈现这个aspx作为视图,而且ViewData中包含了要给视图传递的数据。在那里,我们将所有公告类别数据放在ViewData["Categories"]中。这里可以看到,我们将这些数据取出,并用来呈现页面。至于那个Html.Action,这里先不细说。你只要知道,这个方法可以生成一个链接,其中第一个参数是链接文字,第二个是要链接到的url的Action名,第三个是要链接到的url的控制器名。关于这些,我们以后细细讨论。
      运行这个例子,并将请求url定位到Home/Index,就可以看到运行效果。
      你可能会发现,不需要指定Home/Index,在输入根目录后就直接呈现了这个页面。其实这是因为在默认的路由配置里,Home和Index是默认的控制器名和Action名。以后我们将会讨论路由问题。

小结
      通过上面的过程,我们第一个ASP.NET MVC页面已经能呈现出来了。而且不单纯只是一个页面,其中还呈现了业务逻辑组件返回的数据。
      也许,您对其中许多地方还有困惑。不要着急,在下一篇中,我们做这个系统的步伐先缓一缓,我将用一整篇文章,详细介绍一下ASP.NET MVC中许多重要的概念与原理。

posted on 2010-04-20 17:05  supegong  阅读(190)  评论(0)    收藏  举报