新建数据

源码下载:点我下载

前言

前面讲解了如何创建一个查询页面并给查询页面添加排序、搜索及分页功能。今天我们来讲讲如何向这个列表添加数据。

讲解的顺序将按照添加数据的步骤的时间顺序来进行,方便大家理清逻辑关系。本节将涉及前面讲到的很多知识点,而且还有很多新知识点。帮助大家温故知新,融会贯通。

创建页面预览如下:

新建链接

首先在之前创建好的查询页面上添加一个能够跳转到创建页面的链接,将这个链接添加到标题和搜索栏之间。代码如下:

<h2>Students</h2>

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

@using (Html.BeginForm("Index","Student", FormMethod.Get))
{
    <p>
        Find by name:@Html.TextBox("SearchString",ViewBag.CurrentFilter as string)
        <input type="submit" value="Search"/>
  </p>
}

上面代码中黄色部分就是添加的链接,这个ActionLink生成的HTML代码如下:

<a href="/Company/Create">Add New Worker</a>

可以看到这个链接访问的是CompanyController下的Create Action。下面来创建这个Action。

新建页面Action

打开文件~\Controllers\CompanyController.cs。在这个Controller中添加一个Create Action,如下所示:

public ViewResult Create()
{
       return View();
}

这么简单?对就是这样,创建数据的页面所有信息都需要用户去填,自然不需要传递数据,也就没有什么操作。

这个Action调用了它对应的View,那么我们就来创建这个View。

新建页面View

在~\Views\Company\文件夹下创建Create.cshtml视图,写入如下代码:

@model SlarkInc.Models.Worker
@{
    ViewBag.Title = "Add New Worker";
}
<h2>Add New Worker</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        <hr />
        @Html.ValidationSummary(true)
        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstName)
                @Html.ValidationMessageFor(model => model.FirstName)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName)
                @Html.ValidationMessageFor(model => model.LastName)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.Sex, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EnumDropDownListFor(model => model.Sex)
                @Html.ValidationMessageFor(model => model.Sex)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.Rating, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Rating)
                @Html.ValidationMessageFor(model => model.Rating)
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                @Html.Submit("Submit")
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

View的第1行代码如下所示,引入了Models文件夹里的Worker类。

@model SlarkInc.Models.Worker

为了更好的进行数据操作,Worker类做了改动,改动之后的代码如下:

using System.ComponentModel.DataAnnotations;
namespace SlarkInc.Models
{
    public enum Sex
    {
        Male, Female
    }
    public class Worker
    {
        public int ID { get; set; }
        [Display(Name = "Last Name")]
        [DataType(DataType.Text)]
        [Required]
        public string LastName { get; set; }
        [Display(Name = "First Name")]
        [DataType(DataType.Text)]
        [Required]
        public string FirstName { get; set; }
        [Required]
        public Sex Sex { get; set; }
        public double? Rating { get; set; }
    }
}

代码中第1行引入了DataAnnotations类库,这样我们就可以在Worker类中添加元数据来在View中更好的操作数据。关于这部分内容可以点这里进一步了解。

看View的第13行代码:

@Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })

这里的LabelFor函数用来显示这个数据对应的名称,它会去找Worker类的FirstName对应的数据名称,也就是Model里的第15行:

[Display(Name = "First Name")]

然后用Html把它显示出来,如下:

<label class="control-label col-md-2" for="FirstName">First Name</label>

View中的第15行:

@Html.EditorFor(model => model.FirstName)

会根据Model中的第16行:

[DataType(DataType.Text)]

来决定用哪种input元素来编辑数据,既然是Text类型的,那就用type="text"的input,如下所示:

<input class="text-box single-line" data-val="true" data-val-required="First Name 字段是必需的。"
id="FirstName" name="FirstName" type="text" value="" />

那上面的代码中 "data-val-required="First Name 字段是必需的。"" 这一段是哪来的呢?

这是EditorFor函数读取到Model中的第17行:

[Required]

这一行表示这个数据是必填项,如果不填则会显示信息"First Name 字段是必需的。"。

在View中的第29行用到函数Html.EnumDropDownListFor,如下所示:

@Html.EnumDropDownListFor(model => model.Sex)

这个函数可以把Enum类型的数据在页面上以下拉菜单的形式显示出来供人编辑。

不过这个函数可不是那么容易用,首先Visual Studio的版本必须是2013或者以上的,项目必须用的是MVC5,然后在菜单中选择工具->库程序包管理器-> 管理解决方案的NuGet程序包。如下所示选择联机,在左上角搜索MVC然后安装最新的MVC 5.2.2版。

更新好之后,这个函数就可以正常使用了。它会根据Sex这个Enum变量来生成下拉菜单。这个Enum的定义如下:

public enum Sex
{
     Male, Female
}

那么它生成的下拉菜单代码如下:

<select data-val="true" data-val-required="Sex 字段是必需的。" id="Sex" name="Sex">
<option selected="selected" value="0">Male</option>
<option value="1">Female</option>
</select>

View的第16行代码:

@Html.ValidationMessageFor(model => model.FirstName)

其中ValidationMessageFor函数用来验证数据的有效性。它根据在Model中这个属性的类型来验证输入的值是否符合要求。比如Rating这个属性是Double类型的,那么在输入数据时,如果数据不是数字则会有相应提示,并且不能提交。

View的第6、7、46行是如下所示的不带参数的Form函数结构:

@using (Html.BeginForm()){}

这样的结构如果不带任何参数,则Form会以Post方法提交给本页面对应的Controller和Action,因此其生成的HTML代码就是如下形式:

<form action="/Company/Create" method="post"></form>

在View中使用了Bootstrap的横向表单布局其结构如下:

<div class="form-horizontal">
    <div class="form-group">
         <label class="control-label col-md-2" for=""></label>
          <div class="col-md-10">
          </div>
    </div>
    <div class="form-group">
         <label class="control-label col-md-2" for=""></label>
          <div class="col-md-10">
          </div>
    </div>
</div>

显示出来的效果如下所示:

每一行对应一个属性,左边是属性名,右边是属性对应的编辑框。属性名的col-md-2表示其占Form总宽度的2/12,col-md-10表示其占Form总宽度的10/12。这用到了Bootstrop的栅格系统。栅格系统详细介绍请点这里

View第8行@Html.AntiForgeryToken()函数的作用是抵御网页跨站请求伪造漏洞(CSRF Cross-site request forgery)。这个漏洞可以盗用登录用户身份发送恶意请求。比如一个用户登录了网上银行,然后访问攻击者的网站,网站就会通过登录用户发出请求来获取银行信息。

View第11行使用Html.ValidationSummary(true)。表示只输出Model级的验证错误信息。其具体用法会在后面章节中详细介绍。

上面这几段对Create.cshtml文件中具有代表性的技术知识点做了详细说明,其他行不再赘述,有问题请留言。

添加数据Action

从上面代码可以看出,Create.cshtml页面会把数据提交给当前页面对应的Controller和Action,因此我们就在CompanyController下写处理提交的数据的Action。代码如下:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "FirstName, LastName, Sex, Rating")] Worker worker)
{
    try
    {
        if (ModelState.IsValid)
        {
            db.Workers.Add(worker);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    }
    catch (DataException /* dex */)
    {
        ModelState.AddModelError("unableToSave","Unable to save changes.Try again, and if the problem persists see your system administrator.");
    }
    return View(worker);
}

注意,之前我们已经写了一个Create Action用来进入添加页面,这里的Create不是修改前面的Create。而是新建了一个Action。

第1行在这个Action前加[HttpPost]表示只有以Post方法请求Create Action的时候才会调用这个Action。

第2行ValidateAntiForgeryToken依然是为了防止跨站请求伪造攻击而写的代码。

第3行Action的参数是以worker实例传递的。也就是说Create.cshtml提交的4个值被赋值给work然后把worker传递给Create作为参数。而这个参数前面的[Bind(Include = "FirstName, LastName, Sex, Rating")]是为了防止过多提交(overposting)攻击的。从Create.cshtml的代码可以知道,这个页面只会提交4个值。而黑客可以有办法通过这个页面提交更多的值给当前Action,而这些多出来的值也会存在worker实例中被添加到数据库,这无疑是危险的。因此[Bind(Include = "")]就限定了不管你提交多少值,我这个Action里只接受"FirstName, LastName, Sex, Rating"这4个值。保证了页面的安全性。

第7行ModelState.IsValid表示提交的数据是否有效。比如对于一个类型为数字的属性必须提交一个数字才算是有效。如果提交的数据有效则保存数据并且将页面跳转回Index.cshtml。

第16行ModelState.AddModelError()函数可以给Model添加一条错误信息,函数的第一个参数是key,用于查找这个错误信息,第二个参数是错误信息的具体内容。这个错误信息可以在View中通过Html.ValidationMessage("unableToSave")来访问到。

查看结果

点击下图的"Add New Worker"链接。

放空必填项或者输入不合法数据出现提示信息如下:

填入正确信息如下:

点击Submit按钮,成功添加数据后的结果如下:

结尾

终于又完成了一篇,再接再厉!

喜欢就推荐下吧!