V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OneAPM
V2EX  ›  .NET

7 天玩转 ASP.NET MVC — 第 3 天

  •  5
     
  •   OneAPM · 2015-07-30 09:59:05 +08:00 · 2771 次点击
    这是一个创建于 3413 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目录

    0. 前言

    我们假定你在开始学习时已经阅读了前两天的学习内容。在第 2 天我们完成了关于显示 Employees 列表的项目。

    在第三天,我们将会通过介绍数据访问层和数据入口将它升级到一个新的层次。

    1. 数据访问层

    在真实场景的项目中,如果没有 Database,那么这个项目是未完成的。在我们的项目中,我们还没有谈到数据库。第三天的首个 Lab 将会学习数据库和数据库层。

    这里我们将使用 SQL Server 和 Entity Framework 来创建 Database 和 Database 访问层。

    简单来说,什么是 Entity Framework?

    这是一个 ORM 工具。ORM 代表的是 Object Relational Mapping。即:对象关系映射。

    在 RDBMS 领域中,我们所谈论的 Tables 表格和 Columns 列的这些方面,在 .NET 领域(面向对象领域)中被称为 Classes 类,对象和属性。

    当我们思考任何有关数据驱动应用的方式时,都可以得出以下两种方式:

    • 书写代码来和数据库打交道(被称为数据访问层和数据库逻辑)

    • 书写代码来将数据库数据映射到面向对象中,反之亦然。

    ORM 是一个工具,可以自动做如上两件事。Entity Framework 是微软的 ORM 工具。

    什么是 Code First 方法?

    在 Entity Framework 中,我们可以使用如下三种的任意方法:

    • Database First 方法。创建一个有表,列和关系的数据库。Entity Framework 将会生成对应的 Model 类(业务实体)和数据访问层代码。

    • Model First 方法。在这个方法中,Model 类和它们之间的联系将会被 Model 设计者在 Visual Studio 中被手动定义。然后 Entity Framework 会自动创建数据访问层和拥有表、列以及关系的数据库。

    • Code First 方法。在这个方法中,手动创建 POCO 类。这些类中的关系将会被代码所定义。当应用第一次执行时,Entity Framework 将会自动在数据库服务器上创建数据访问层和拥有表、列以及关系的数据库。

    什么是 POCO 类?

    POCO 代表的是「Plain Old CLR Objects」。POCO 类代表的是我们所创建的简单 .NET 类。在我们之前的例子中, Employee 类是一个简单的 POCO。

    2. Lab 8 — 向项目中添加数据访问层

    第一步:创建数据库

    连接 SQL Server 然后创建一个新的数据库,命名为「SalesERPDB」。

    7 天玩转 ASP.NET MVC — 第 3 天

    第二步:创建 ConnectionString

    打开 Web.config 文件,然后在 Configuration 区域内添加如下片段:

    <connectionStrings>
    <add connectionString="Data Source=(local);Initial Catalog=SalesERPDB;Integrated Security=True"
            name="SalesERPDAL"       
            providerName="System.Data.SqlClient"/>
    </connectionStrings>
    

    第三步:添加 Entity Framework 引用

    右击项目-> 管理 Nuget 包。搜索 Entity Framework,然后点击安装。

    7 天玩转 ASP.NET MVC — 第 3 天

    第四步:创建数据访问层

    • 在根目录下创建一个新文件夹,命名为「DataAccessLayer」,然后在里面创建一个新的类,命名为「SalesERPDAL」。

    • 在类顶部写引用声明如下
      using System.Data.Entity;

    • 继承 DbContext 的类「SalesERPDAL」

      public class SalesERPDAL: DbContext
      {
      }

    第五步:为 Employee 类创建主键

    打开 Employee 类并在类顶部声明如下:

    using System.ComponentModel.DataAnnotations;
    

    在 Employee 类中添加 EmployeeId 属性,然后将其标注为 Key 属性。

    public class Employee
    {
        [Key]
        public int EmployeeId  { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Salary { get; set; }
    }
    

    第六步:定义映射

    在「SalesERPDAL」类中添加如下声明语句:

    using WebApplication1.Models;
    

    在 SalesERPDAL 类中重写 OnModelCreating 方法。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<employee>().ToTable("TblEmployee");
        base.OnModelCreating(modelBuilder);
    }
    

    注意:上述代码中的片段「TblEmployee」代表的是表名。在运行时讲自动被创建。

    第七步:在数据库中创建 Employees 属性

    在「SalesERPDAL」类中创建一个新属性,命名为 Employee,如下所示:

    public DbSet<employee> Employees{get;set;}
    

    DbSet 将会展示所有可以在数据库中查询到的 Employees。

    第八步:改变业务层代码,从数据库中读取数据

    打开 EmployeeBusinessLayer 类,在顶部加上声明如下:

    using WebApplication1.DataAccessLayer;
    

    现在改变 GetEmployees 方法如下:

    public List<employee> GetEmployees()
    {
        SalesERPDAL salesDal = new SalesERPDAL();
        return salesDal.Employees.ToList();
    }
    

    第九步:执行并测试

    按下 F5,并执行应用。

    7 天玩转 ASP.NET MVC — 第 3 天

    现在的数据库中,我们没有任何的 Employees,所以我们看见的是一个空白的 Grid。

    查看数据库,现在我们可以在 TblEmployee 表中看到所有的列。

    7 天玩转 ASP.NET MVC — 第 3 天

    第十步:插入测试数据

    向 TblEmployee 表中插入一些测试数据。

    7 天玩转 ASP.NET MVC — 第 3 天

    第十一步:执行并测试应用

    按下 F5 并再次运行应用。

    7 天玩转 ASP.NET MVC — 第 3 天

    Lab 8 的 Q&A

    什么是 DbSet?

    DbSet 简单地表示了可以从数据库中查询到的实体集合。当我们再次写一个 Linq 查询时,DbSet 对象会对查询进行内存转换,然后触发数据库。

    在我们的例子中,「Employee」是一个 DbSet,它承载了所有可以从数据库中查询到的 Employee 实体对象。每一次我们尝试访问「Employees」时,它都将从“TblEmployee”表中获取记录,然后将其转换为「Employees」对象并返回集合。

    数据库连接串和数据访问层是如何连接的?

    Mapping 通过名称来实现。在我们的例子中,ConnectionString 名称和数据访问层类的名称是一样的,即「SalesERPDAL」,因此它们是自动映射的。

    我们可以更改 ConnectionString 的名称吗?

    答案是肯定的。在这个例子中,我们需要在数据访问层类中定义一个构造函数如下:

    public SalesERPDAL():base("NewName")
    {
    }
    

    3. 组织所有

    我们需要做几个改变,使得所有是有组织和有意义的。

    第一步:重命名

    • 「TestController」换名为 「EmployeeController」。

    • GetView 行为方法改为 Index。

    • Test 文件夹(Views 文件夹下) 改为 Employee

    • 「MyView」视图改为「Index」。

    第二步:从 EmployeeListViewModel 中删除 UserName 属性

    第三步:从视图中删除 UserName

    打开 View/Employee.Index.cshtml 视图,然后从中删除 UserName。

    简单来说,就是删除如下代码:

    Hello @Model.UserName
    <hr />
    

    第四步:在 EmployeeController 中更改 Index 行为方法

    更改 EmployeeController 中的 Index 行为方法如下:

    public ActionResult Index()
    {
        &hellip;&hellip;
        &hellip;&hellip;
        &hellip;&hellip;
        employeeListViewModel.Employees = empViewModels;
        //employeeListViewModel.UserName = "Admin";-->Remove this line -->Change1
        return View("Index", employeeListViewModel);//-->Change View Name -->Change 2
    }
    

    现在执行的 URL 将会为:「…/Employee/Index」。

    4. Lab 9 — 创建 Data Entry Screen

    第一步:创建 Action 方法

    在 EmployeeController 中创建一个 Action 方法,命名为「AddNew」,如下:

    public ActionResult AddNew()
    {
        return View("CreateEmployee");
    }
    

    第二步:创建 View

    在文件夹 View/Employee 下创建一个 View,命名为「CreateEmployee」。代码如下:

    @{
        Layout = null;
    }
    <!DOCTYPE html>
    <html>
        <head>
        <meta name="viewport" content="width=device-width" />
        <title>CreateEmployee</title>
        </head>
        <body>
          <div>
            <form action="/Employee/SaveEmployee" method="post">
            First Name: <input type="text" id="TxtFName" name="FirstName" value="" /><br />
            Last Name: <input type="text" id="TxtLName" name="LastName" value="" /><br />
            Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />
            <input type="submit" name="BtnSave" value="Save Employee" />
            <input type="button" name="BtnReset" value="Reset" />
            </form>
          </div>
        </body>
    </html>
    

    第三步:在 Index 视图中创建一个链接

    打开 Index.cshtml,然后增加一个超链接指向 AddNew 行为的URL。

    <ahref="/Employee/AddNew">Add New</a>
    

    第四步:执行并测试应用

    按下 F5 并执行应用。

    7 天玩转 ASP.NET MVC — 第 3 天

    Lab 9 的 Q&A

    Form 标签的目的是什么?

    在第一天的系列学习中,我们已经明白了「Web 世界不会遵循事件驱动编程模型。它遵循的是请求响应模型。终端用户发出请求,然后服务器给出响应。」Form 标签是 HTML 中做出响应的其中一种方式。只要标签里的提交按钮被点击,一个请求就将发送给动作属性中指定的 URL 中。

    Form 标签中的方法属性是什么?

    它决定了请求的类型。请求也许是如下的其中一种:get、post、put 或者是 delete。

    • Get:当我们想获取什么数据时

    • Post:当我们想创建什么数据时

    • Put:当我们想更新什么数据时

    • Delete:当我们想删除什么数据时

    运用 Form 标签和通过浏览器地址栏或者超链接来发出请求,有何区别?

    当我们使用 Form 标签来发送请求时,所有输入控件中的值都会伴随着请求一起被处理。

    Checkbox、Radio 按钮和 Dropdowns 控件中的值也会被发送吗?

    答案是肯定的。所有输入控件(输入类型为 Text,Radio,Checkbox)以及 Dropdowns(表示的是被选中的元素)都会被发送。

    输入的值如何发送给服务器?

    当请求的类型是 Get、Put 或者 Delete 时,输入的值会以查询字符串参数的方式发送。

    当请求的类型是 Post 时,输入的值会以 Post 数据发送。

    输入控件中的 Name 属性的目的是什么?

    就像我们之前所谈论的,当按钮被点击时,输入控件中的值将会随着请求一起被发送。这会使得服务器在这个时刻接收到多于一个的值。为了在发送值的时候,单独区别每一个值,就会为它们附加上一个 Key,这个 Key 就是「Name」属性。

    Name 和 Id 属性的目的是否是相同的?

    答案是否定的。就像刚才的问题所说,当请求发生时,「Name」属性被 HTML 所使用,而「Id」属性被开发者所使用,为一些 JavaScript 实现一些动态功能。

    「input type = submit」 和 「input type = button」有什么区别?

    当我向服务器发送请求时,Submit 按钮会被特殊用到。而一个简单的按钮是用来处理一些客户端的行为的。简单的按钮不会自己做一些事情。

    5. Lab 10 — 在服务器/Controller 获取 Posted 数据

    第一步:创建 SaveEmployee 行为方法

    在 Employee 控制器中创建一个行为方法,命名为 SaveEmployee,代码如下:

    public string SaveEmployee(Employee e)
    {
       return e.FirstName + "|"+ e.LastName+"|"+e.Salary;
    }
    

    第二步:执行并测试

    按下 F5 并执行应用。

    7 天玩转 ASP.NET MVC — 第 3 天

    Lab 10 的 Q&A

    在 Action 方法里,Textbox 的值是如何更新 Employee 对象的?

    在 ASP.NET MVC 中,存有一个概念,叫做 Model Binder。

    • 无论何时一个包含参数的请求向 Action 方法发送时,Model Binder 都会自动执行。

    • Model Binder 将会遍历方法的所有原始参数,然后将它们与发送过来的数据的参数的名称相对比。(发送过来的数据意味着要么是 Posted 数据,或者是查询字符串)。当匹配成功时,会依照发送过来的数据分配给参数。

    • 当 Model Binder 遍历完每一个类参数中的每一个属性后,然后和发送过来的数据做对比。当匹配成功后,依照发送过来的数据分配给参数。

    当两个参数是特指的,将会发生什么?例如第一个是「Employee」,第二个是「FirstName」?

    FirstName 将会在初始的变量 FirstName 中更新,也会在 e.FirstName 属性中更新。

    ModelBinder 可以和组合关系一起运用吗?

    答案是肯定的。但是在这个情形下控件的名称应该被给出。例如:

    Customer 和 Address 类的代码如下:

    public class Customer
    {
        public string FName{get;set;}
        public Address address{get;set;}
    }
    public class Address
    {
        public string CityName{get;set;}
        public string StateName{get;set;}
    }
    

    在这种情形下,HTML 如下:

    ...
    ...
    ...
    <input type="text" name="FName">
    <input type="text" name="address.CityName">
    <input type="text" name="address.StateName">
    ...
    ...
    ...
    

    6. Lab 11 — 重置和取消按钮

    第一步:开始重置和取消按钮

    增加一个重置和取消按钮,代码如下:

    ...
    ...
    ...
    <input type="submit" name="BtnSubmit&rdquo; value="Save Employee" />
    
    <input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
    
    <input type="submit" name="BtnSubmit" value="Cancel" />
    

    注意:保存和取消按钮都有相同的「Name」属性,即「BtnSubmit」。

    第二步:定义 ResetForm 方法

    在 HTML 顶部区域增加一个 Script 标签,用于创建一个 JavaScript 方法,命名为 ResetForm。代码如下:

    <script>
        function ResetForm() {
            document.getElementById('TxtFName').value = "";
            document.getElementById('TxtLName').value = "";
            document.getElementById('TxtSalary').value = "";
        }
    </script>
    

    第三步:在 EmployeeController 的 SaveEmployee 行为方法中实现取消点击事件。

    改变 SaveEmployee 行为方法如下:

    public ActionResult SaveEmployee(Employee e, string BtnSubmit)
    {
        switch (BtnSubmit)
        {
            case "Save Employee":
                return Content(e.FirstName + "|" + e.LastName + "|" + e.Salary);
            case "Cancel":
                return RedirectToAction("Index");
        }
        return new EmptyResult();
    }
    

    第四步:执行应用

    按下 F5 并执行应用。通过点击“Add New”链接导航到 AddNew 屏幕。

    第五步:测试重置功能

    7 天玩转 ASP.NET MVC — 第 3 天

    第六步:测试 Save 和 Cancel 功能

    7 天玩转 ASP.NET MVC — 第 3 天

    Lab 11 的 Q&A

    为什么保存和取消按钮的名称是一样的?

    我们知道,一旦提交按钮被点击,一个请求就会被发送到服务器端。并且伴随着请求,所有输入控件的值也一起被发送。

    Submit 按钮也是一个输入按钮。因为按钮的值也会被发送。

    当保存按钮被点击时,保存按钮的值,即「Save Employee」将会随着请求一起被发送。当取消按钮被点击时,取消按钮的值,即「Cancel」将会随着请求一起被发送。

    在 Action 方法中。Model Binder 将会处理这些工作。它将会依照输入的值(伴随着请求)更新参数的值。

    实现多个提交按钮的方式是什么?

    这里有多个方式。我介绍其中三种。

    • 隐藏 Form 元素

    第一步:在视图中创建一个隐藏 Form 元素

    <form action="/Employee/CancelSave" id="CancelForm" method="get" style="display:none">
    </form>
    

    第二步:改变 Submit 按钮为一个常规按钮,并且通过 JavaScript 将上面的 Form 发送

    <input type="button" name="BtnSubmit" value="Cancel" onclick="document.getElementById('CancelForm').submit()" />
    
    • 运用 JavaScript 动态地改变动作 URL

      <form action="" method="post" id="EmployeeForm" >
      ...
      ...
      <input type="submit" name="BtnSubmit" value="Save Employee" onclick="document.getElementById('EmployeeForm').action = '/Employee/SaveEmployee'" />
      ...
      <input type="submit" name="BtnSubmit" value="Cancel" onclick="document.getElementById('EmployeeForm').action = '/Employee/CancelSave'" />
      </form>

    • Ajax

    不再运用 Submit 按钮,取而代之的是简单的输入按钮,然后运用 JQuery 或者其它库来实现纯 Ajxa 请求。

    为什么我们在实现重置功能时,没使用「input type = reset」?

    「input type = reset」控件不会清除值,它只是将控件的值改为默认的值。例如:

    <input type="text" name="FName" value="Sukesh">
    

    在这个例子中,控件的默认值是「Sukesh」。

    如果我们运用「input type = reset」来实现重置功能,那么每一次点击重置按钮,默认的值「Sukesh」将会被设置到 Textbox 中。

    当名称没有与类中的属性名称匹配时,会怎样?

    这是一个在面试中经常被问到的常规问题。

    例如我们有一段 HTML 代码如下:

    First Name: <input type="text" id="TxtFName" name="FName" value="" /><br />
    Last Name: <input type="text" id="TxtLName" name="LName" value="" /><br />
    Salary: <input type="text" id="TxtSalary" name="Salary" value="" /><br />
    

    现在我们的 Model 类包含的属性名称有 FirstName,LastName 和 Salary。因此默认的 Model Binder 将不会在这里处理。

    在这种情形下,我们有三种解决方案:

    • 在 Action 方法内部,运用 Request.Form 语法来检索发送过来的值,然后手动构造 Model 对象如下:

      public ActionResult SaveEmployee()
      {
      Employee e = new Employee();
      e.FirstName = Request.Form["FName"];
      e.LastName = Request.Form["LName"];
      e.Salary = int.Parse(Request.Form["Salary"])
      ...
      ...
      }

    • 运用参数名称,然后手动创建 Model 对象如下:

      public ActionResult SaveEmployee(string FName, string LName, int Salary)
      {
      Employee e = new Employee();
      e.FirstName = FName;
      e.LastName = LName;
      e.Salary = Salary;
      ...
      ...
      }

    • 创建自定义的 Model Binder 来替换默认的 Model Binder。

    第一步:创建自定义的 Model Binder

    public class MyEmployeeModelBinder : DefaultModelBinder
    {
        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
            Employee e = new Employee();
            e.FirstName = controllerContext.RequestContext.HttpContext.Request.Form["FName"];
            e.LastName = controllerContext.RequestContext.HttpContext.Request.Form["LName"];
            e.Salary = int.Parse(controllerContext.RequestContext.HttpContext.Request.Form["Salary"]);
            return e;
        }
    }
    

    第二步:用这个新的 Model Binder 来替换默认的 Model Binder

    public ActionResult SaveEmployee([ModelBinder(typeof(MyEmployeeModelBinder))]Employee e, string BtnSubmit)
    {
        ......
    }
    

    RedirectToFunction 函数是做什么的?

    它用来产生 RedirectToRouteResult,就像 ViewResult 和 ContentResult一样(在第一天学习中探讨)。RedirectToRouteResult 是 ActionResult 的子类,它代表的是间接的响应。当浏览器接到 RedirectToRouteResult,它就会产生新的请求到一个新的行为方法。

    注:这里浏览器对新的请求是有责任的,因此 URL 将会更新到一个新的 URL。

    什么是 EmptyResult?

    它是 ActionResult 的其中一个子类。当浏览器接到的响应是 EmptyResult 时,它将会简单地呈现一个空白屏幕。它简单地代表「No Result」。

    在我们的例子中,这种情形不会发生。只要确保所有的代码路径返回的值。

    注:当 Action 方法返回的值是空的,结果等同于 EmptyResult。

    7. Lab 12 — 在数据库中保存记录并更新 Grid

    第一步:在 EmployeeBusinessLayer 中创建 SaveEmployee

    public Employee SaveEmployee(Employee e)
    {
        SalesERPDAL salesDal = new SalesERPDAL();
        salesDal.Employees.Add(e);
        salesDal.SaveChanges();
        return e;
    }
    

    第二步:改变 SaveEmployee 行为方法

    在 EmployeeController 中,改变 SaveEmployee 行为方法,代码如下:

    public ActionResult SaveEmployee(Employee e, string BtnSubmit)
    {
        switch (BtnSubmit)
        {
            case "Save Employee":
                EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
                empBal.SaveEmployee(e);
                return RedirectToAction("Index");
            case "Cancel":
                return RedirectToAction("Index");
        }
        return new EmptyResult();
    }
    

    第三步:执行并测试

    按下 F5 并执行应用。导航到 Data 入口屏幕并输入一些合法的值。

    7 天玩转 ASP.NET MVC — 第 3 天

    8. Lab 13 — 增加服务器端认证

    在 Lab 10 中,我们已经了解了 Model Binder 的基本功能。现在我们来更多地了解下。

    • Model Binder 通过发送过来的数据来更新 Employee 对象。

    • 但是这个不是 Model Binder 的唯一功能。Model Binder 还更新 ModelState。

    • 它有一个属性,称为 IsValid,这个决定了 Model(即 Employee 对象)是否更新成功了。如果服务器端的认证失败了,Model 将不会更新。

    • 它承载了认证错误。例如:ModelState["FirstName"].Errors 包含了与 First Name 相关的错误。

    • 它承载了发送过来的数据值。(Posted 数据或者是查询字符串数据)

    在 ASP.NET MVC 中,我们运用 DataAnnotations 来实现服务器端的认证。

    在了解 Data Annotation 之前,我们先了解一些 Model Binder。

    ModelBinder 如何处理初始数据类型?

    当 Action 方法包含初始类型参数时,Model Binder 将会把参数的名称与发送过来的数据进行对比。(发送过来的数据是 Posted 数据或者是查询字符串)

    • 当匹配成功时,将会依照发送过来的数据分配给参数。

    • 当匹配失败时,参数将会被分配给默认值。(对于整型的默认值是0,对于字符串的默认值是 null)

    • 当数据类型不匹配的异常被抛出时,这种情况下不会进行分配操作。

    Model Binder 如何处理类?

    当参数是一个类参数,Model Binder 将会遍历所有类的所有属性,并且将每一个属性名称与发送过来的数据做对比。

    • 当匹配成功时,如果发送过来的数据是空的。

    Null 值将会被分配给属性。如果不能分配,默认的值将会被设置,并且 ModelState.IsValid 将会被设置为 false。

    当 Null 值可以分配给属性时,这将会被视作不合法的值,因为属性附上了认证,因此 ModelState.IsValid 将会被设置为 false。

    • 当匹配成功时,发送过来的数据不为空。

    当数据类型不匹配时,将不会分配值。或者服务器端的认证失败,将分配 Null 值。此时 ModelState.IsValid 将会被设置为 false。如果不能分配 Null 值,默认的值将会被设置。

    • 当匹配不成功时,参数将会被分配为默认值。(对于整型的默认值是0,对于字符串的默认值是 null)。在这种情形下,ModelState.IsValid 将不会受到影响。

    现在让我们了解一下如何向项目中增加认证功能。

    第一步:运用 DataAnnotations 装饰属性。

    在 Model 文件夹下打开 Employee 类,运用 DataAnnotation 来装饰 FirstName 和 LastName,代码如下:

    public class Employee
    {
    ...
    ...
        [Required(ErrorMessage="Enter First Name")]
        public string FirstName { get; set; }
    
        [StringLength(5,ErrorMessage="Last Name length should not be greater than 5")]
        public string LastName { get; set; }
    ...
    ...
    }
    

    第二步:改变 SaveEmployee 行为方法。

    打开 EmployeeController,改变 SaveEmloyee 行为方法如下:

    public ActionResult SaveEmployee(Employee e, string BtnSubmit)
    {
        switch (BtnSubmit)
        {
            case "Save Employee":
                if (ModelState.IsValid)
                {
                    EmployeeBusinessLayer empBal = new EmployeeBusinessLayer();
                    empBal.SaveEmployee(e);
                    return RedirectToAction("Index");
                }
                else
                {
                    return View("CreateEmployee ");
                }
            case "Cancel":
                return RedirectToAction("Index");
        }
        return new EmptyResult();
    }
    

    注:正如你所看见的,当 SaveEmployee 按钮点击后, ModelState.IsValid 失败,ViewResult 指向「CreateEmloyee」视图。

    第三步:在视图中呈现错误

    改变「Views/Index/CreateEmployee.cshtml」中的代码如下。

    这次我们将会运用「Table」标签来格式化一下 UI。

    <table>
       <tr>
          <td>
             First Name:
          </td>
          <td>
             <input type="text" id="TxtFName" name="FirstName" value="" />
          </td>
       </tr>
       <tr>
          <td colspan="2" align="right">
            @Html.ValidationMessage("FirstName")
          </td>
       </tr>
       <tr>
          <td>
            Last Name:
          </td>
          <td>
             <input type="text" id="TxtLName" name="LastName" value="" />
          </td>
       </tr>
       <tr>
          <td colspan="2" align="right">
             @Html.ValidationMessage("LastName")
          </td>
       </tr>
    
       <tr>
          <td>
            Salary:
          </td>
          <td>
             <input type="text" id="TxtSalary" name="Salary" value="" />
          </td>
       </tr>
       <tr>
          <td colspan="2" align="right">
            @Html.ValidationMessage("Salary")
          </td>
       </tr>
    
       <tr>
          <td colspan="2">
            <input type="submit" name="BtnSubmit" value="Save Employee" />
            <input type="submit" name="BtnSubmit" value="Cancel" />
             <input type="button" name="BtnReset" value="Reset" onclick="ResetForm();" />
          </td>
       </tr>
    </table>
    

    因文章字数限制无法显示全文,点击查看所有

    英文原文地址:Learn MVC Project in 7 days

    本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客

    2 条回复    2015-07-31 17:25:49 +08:00
    ob
        1
    ob  
       2015-07-30 11:55:54 +08:00
    支持一下!
    idamien
        2
    idamien  
       2015-07-31 17:25:49 +08:00
    好像还不错, 玩转这个词有点大吆
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6066 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 03:15 · PVG 11:15 · LAX 19:15 · JFK 22:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.