MVC:使用模型来建立前台页面

Come fron old posts in MVC:使用模型来建立前台页面  , 2016-09-14

很久没有看MVC了,之前学了一点就搁置了,今天继续捡起来,毕竟Mvc还是很重要很重要的,也是接下去一段时间我应该着重花力气去学习的。

上一次的MVC博客已经把实体框架的运用交代了,那么接下来我需要让网页真正成为网页,而不再是一堆数据表。

————–以上是草稿,二十三天前的,太颓废了!————–

下面正经开始写。

在.NET MVC 中可以通过几种方式来构建前台网页,比如强类型,动态或者使用内置的ViewBag来风骚地构建。我因为之前写UWP的关系所以还是比较适应用强类型来写,辅以ViewBag看来偷个懒,感觉还是很舒服的。动态的话似乎就是更纯粹地web编程习惯了,不是很了解,先不管。我们来看看怎么使用强类型地逻辑构建我们的网页

通过前几次的blog,我们已经建立了实体类,并且搞定了数据库,所有需要的数据都已经完备了。那么基于EntityFramework,我们需要建立一个控制器(controller),来驱动数据和前台的交互。在VS可以通过建立控制器之间完成前台页面增删改查地建立,实在是太偷懒了,也不适合我学习,所以我使用的是单纯建立控制器,而不建立视图,自己一句一句写。

以SchoolData模型来建立控制器,得到SchoolDataController:

namespace MVCTest001.Controllers {
    public class SchoolDataController : Controller {
        SCDBContext scdb = new SCDBContext();

    }
}

先来里面什么都没有啊,但是考虑到数据库上下文肯定是要经常用到的,所以在字段里面先声明好,以备后续操作的使用。

考虑到数据库的四个基本操作,我们需要建立4个页面和一个默认主页,分别负责增删改查部分,这里就不写了,级的建立的是*.cshtml类型的网页,使用Razor语法来实现。

首先从首页-Index.cshtml来开始

1.DropDownList的实现

因为主页名是Index,所以我们需要在控制器里增加一个Index方法,来通过控制器路由指向这个页面,这个地方很好理解。我想要在Index页面显示数据库里面SchoolData部分的信息展示,还希望通过姓名来筛选显示,需要一个DropDownList来实现。需求很明确,我们该怎么做呢?

先是一个客户端思维地实现DropDownList地方式,彻底的强类型。首先形成list内容地数据源,并通过ViewBag交给页面部分:

var FirstNamesList = new List<string>();
            var FirstSearchData = from item in scdb.Students
                             orderby item.FirstMidName
                             select item.FirstMidName;

            FirstNamesList.AddRange(FirstSearchData.Distinct());
            ViewBag.FirstNamsList = new SelectList(FirstNamesList);

然后在页面部分取出转化成对应的类型,通过内置辅助类形成DropDownList,并构建Get表单:

@using (Html.BeginForm("Index", "SchoolData", FormMethod.Get)) {

        <div class="col-md-10">
            姓: @Html.DropDownList("firstName", ViewBag.FirstNamsList as SelectList, htmlAttributes: new { @class = "form-control" })
            名: @Html.TextBox("lastName", null, htmlAttributes: new { @class = "form-control" })
            <br />
            <br />
            <input type="submit" value="筛选" class="btn btn-default" />
        </div>
    }

上面的做法我是完全按照自己的思路来写的,和web正常的写法相比,的确是很古怪,但的确可以运行实现。

接下来我们看看官方的实现方法,这里是CourceData地实现,逻辑是类似的:

var listResource = db.Departments.ToList();
            ViewBag.SelectedDepartment = new SelectList(listResource, "DepartmentID", "Name", SelectedDepartment);
@using (Html.BeginForm()) {
    <p>
        Select Department: @Html.DropDownList("SelectedDepartment", "All")
        <input type="submit" value="Filter" />
    </p>
}

不同的地方是很明显的,这个list,显示的内容是“Name”字段,但每个Item实际上是“DepartmentID”,选中的也是这个,最终提交地也是ID。而我那个做法显示地内容和值内容都是相同的,显然通过控制器,后者更能适应Web。

2.按照DropDownList的选择来GET数据

这里还没有结束,因为Submit之后将重新GET一次页面信息,所以我们需要根据GET的内容来完成Index方法地其余部分。

如果是我的写法,那么接下来:

// GET: SchoolData
        [HttpGet]
        public ActionResult Index(string firstName,string lastName) {
            // for DropdownList source
            var FirstNamesList = new List<string>();
            var FirstSearchData = from item in scdb.Students
                             orderby item.FirstMidName
                             select item.FirstMidName;

            FirstNamesList.AddRange(FirstSearchData.Distinct());
            ViewBag.FirstNamsList = new SelectList(FirstNamesList);

            // for Content Data in tsble
            var studentData = from item in scdb.Students
                              orderby item.FirstMidName
                              select item;

            if (!string.IsNullOrEmpty(firstName))
                studentData = studentData
                    .Where(i => i.FirstMidName.Contains(firstName))
                    .OrderBy(s=>s.FirstMidName);

            if (!string.IsNullOrEmpty(lastName))
                studentData = studentData
                    .Where(i => i.LastName.Contains(lastName))
                    .OrderBy(s => s.FirstMidName);

            return View(studentData);
        }

我传入的参数分别是“firstName”和“lastName”,经过判断,重新查询数据库上下文,并返回正确的视图,当然如果没有参数的情况也要考虑到。

如果按照默认的方式,Get方法应该是这样的:

// GET+POST: CourseData/Index
        public ActionResult Index(int? SelectedDepartment) {
            var listResource = db.Departments.ToList();
            ViewBag.SelectedDepartment = new SelectList(listResource, "DepartmentID", "Name", SelectedDepartment);
            int value = SelectedDepartment.GetValueOrDefault();

            var courses = db.Courses
                .Where(i => !SelectedDepartment.HasValue || i.DepartmentID == value)
                .OrderBy(s => s.CourseID)
                .Include(c => c.Department);

            return View(courses.ToList());
        }

由于传入的是ID,所以使用ID(ID是当前数据表的主键,所以很方便)获取试图更新就显得非常简单了。

3.将GET到的数据以表格方式呈现

那么通过GET方法得到了我们需要去呈现的数据,下一步该怎么做?

如果是我的GET方法,获取到的是一个SchoolData(IOrderedQueryable<Student>)对象,如果通过内置方法,得到的是courses.ToList(),就是一个List对象。都是一个类似List逻辑的集合对象,而且通过了View返回到前台视图了,这就好办了。

由于是强类型方式,我们可以在前台定义内置的视图模型Model

@model IEnumerable<MVCTest001.Models.Course>

这里强制将我传送过来的集合数据类型进行转化,公开了枚举数,这样很利于foreach进行集合迭代。这里使用Razor语法,而不是脚本来编程构建网页的表格内容:

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.FullName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstMidName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.LastName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.EnrollmentDate)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ID)
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.FullName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ID)
            </td>
            <td>
                @Html.ActionLink("编辑", "Edit", new { id = item.ID }) |
                @Html.ActionLink("细节", "Details", new { id = item.ID }) |
                @Html.ActionLink("删除", "Delete", new { id = item.ID })
            </td>
        </tr>
    }

</table>

最后获取到的是这样的HTML显示效果:

 

jhvcvcjkhc

通过POST将修改提交到服务器

如果需要从客户端修改服务端的数据,就可以使用HttpPost的方式,那么POST在MVC是如何来构建的呢?

我们先以创建数据条目来进入话题

1.首先以GET获得Create准备页面

首先我们需要通过控制器将页面导航到Create页面,我们需要在Index页面加入一个链接:

<p>@Html.ActionLink("添加学生信息", "Create")</p>

这里调用的是ActionLink的一个重载方法,省略了控制器,那么就表示在本控制器内部进行跳转。显示的Html内容是“添加学生信息”,指向的ActionName(方法名)是“Create”。

所以我们在控制其内部增加一个“Create”方法,返回“Create.cshtml”页面视图:

// 很简单......这的确有点智障
public ActionResult Create() {
            return View();
        }

当然光有这样一个方法其实是不够的,因为导航到的Create页面,所有的内容都还没有所以必须完成一个Post方法来提交数据到服务器。

2.完成POST方法

不过首先我们应该来看看前台的Html的结构:

@model MVCTest001.Models.Student

@{
    ViewBag.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>添加学生信息</h2>
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()

    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

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

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

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

    <div class="form-group">
        <input type="submit" value="确认" class="btn btn-default" />
    </div>

}

<div class="form-group">
    @Html.ActionLink("放弃并退回", "Index")
</div>

Html.ValidationMessageFor()是错误检查,通过定义模型时候的特性来检查输入是否符合规则,剩下的很好理解了,一个条目一个坑,自己填上去然后Submit。那么如何来构造Post的后台方法呢?

如果是按照我的写法,那应该是把输入的个条数据通过表单传到后台,写入模型实例对应的字段,然后调用SaveChanges来保存数据。然而MVC在这方面是有完全不同的变化的,MVC提供了一整套简化的操作,不再需要一一手动去操作字段了。具体是怎样的我们来看这段代码:

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(
            [Bind(Include = "FirstMidName,LastName,ID,EnrollmentDate,FullName,Enrollments")]
            Student student) {
            if (ModelState.IsValid) {
                scdb.Students.Add(student);
                scdb.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(student);
        }

首先通过特性来限定方法体是一个HttpPost方法,在通过特性声明出一个AntiForgeryToken产生一个虚拟的表框以简单地防止非网页地POST提交,是对应前台HTML代码中@Html.AntiForgeryToken()。

这里有看到了传入的参数是一个Student实例,并且通过特性,提供了有关于绑定到模型地信息。

[Bind(Include = "FirstMidName,LastName,ID,EnrollmentDate,FullName,Enrollments")]

MVC框架通过指明给定的字段栏位,和前台一一对应,将改变先缓存到上下文中地当前model,在调用SaveChange的时候完成数据的更新,而不再需要意义手动去修改或者创建。

ModelState.IsValid 当模型状态字典返回的实力经检查有效的时候,我们就可以调用数据库操作并保存我们的改动,并将页面重定向到Index页面;否则,返回当前的实力到前台,就像前面的GET一样,让用户有机会修改,当然还会提示错误的信息。

Create部分就这样完成了,那同理修改删除也就类似了,只是操作数据库变更的那条指令会有区别。整个数据库单表增删改查功能就算是完成了。

375 thoughts on “MVC:使用模型来建立前台页面”

Leave a Reply

Your email address will not be published. Required fields are marked *