1. Model

1.1 添加一个模型

注意,添加属性时可以输入"prop",会自动输入代码段。

public class CheckoutAccount
{
public int Id { get; set; } public string AccountNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Balance { get; set; }
}

1.2 添加一个“包含读写操作的MVC 5控制器”CheckingAccountController

去掉Details的参数Id,因为现在我没还没有数据库。

public ActionResult Details()
{
return View();
}

1.3 在Details上添加视图

在Details动作上右键,添加视图:

  • 视图名称Details
  • 模板Details
  • 模型CheckoutAccount
  • 数据上下文类:空
  • 创建为分布视图:不勾选
  • 引用脚本库:不勾选
  • 使用布局页:默认布局
@model AspNetMVCEssential.Models.CheckoutAccount

@{
ViewBag.Title = "Details";
} <h2>Details</h2> <div>
<h4>CheckoutAccount</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.AccountNumber)
</dt> <dd>
@Html.DisplayFor(model => model.AccountNumber)
</dd> <dt>
@Html.DisplayNameFor(model => model.FirstName)
</dt> <dd>
@Html.DisplayFor(model => model.FirstName)
</dd> <dt>
@Html.DisplayNameFor(model => model.LastName)
</dt> <dd>
@Html.DisplayFor(model => model.LastName)
</dd> <dt>
@Html.DisplayNameFor(model => model.Balance)
</dt> <dd>
@Html.DisplayFor(model => model.Balance)
</dd> </dl>
</div>
<p>
@Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
@Html.ActionLink("Back to List", "Index")
</p>

说明:

  • 第一行代码指明模板Model类型,任何@Model都是CheckoutAccount类型。
  • Html.DisplayNameFor(model => model.AccountNumber)模型属性的名称
  • Html.DisplayFor(model => model.AccountNumber)模型属性的值

1.4 添加首页到Details页面的链接

<div class="col-md-6 margin-top-20">
<a href="@Url.Action("Details", "CheckingAccount")" class="btn btn-primary btn-lg btn-block"><span class="glyphicon glyphicon-question-sign"></span>余额查询</a>
</div>

@Url.Action("action", "controller")返回URL,而不是<a>


2. 显示和验证模型属性

2.1 清理Details视图

@model AspNetMVCEssential.Models.CheckoutAccount

@{
ViewBag.Title = "Details";
}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h2>账户查询</h2>
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.AccountNumber)
</dt> <dd>
@Html.DisplayFor(model => model.AccountNumber)
</dd> <dt>
@Html.DisplayNameFor(model => model.FirstName)
</dt> <dd>
@Html.DisplayFor(model => model.FirstName)
</dd> <dt>
@Html.DisplayNameFor(model => model.LastName)
</dt> <dd>
@Html.DisplayFor(model => model.LastName)
</dd> <dt>
@Html.DisplayNameFor(model => model.Balance)
</dt> <dd>
@Html.DisplayFor(model => model.Balance)
</dd>
</dl>
</div>
</div>

2.2 修改Model

[Display(Name="Account")]

public class CheckoutAccount
{
public int Id { get; set; } [Display(Name = "账户")]
public string AccountNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; }
[Display(Name = "姓名")]
public string Name { get { return FirstName + " " + LastName; } }
[DataType(DataType.Currency)]
public int Balance { get; set; }
}

2.3 创建Create对应的视图

此时要选择引用脚本库。

@using (Html.BeginForm())
{
@Html.AntiForgeryToken() <div class="form-horizontal">
<h4>CheckoutAccount</h4>
<hr />
@Html.ValidationSummary(true) <div class="form-group">
@Html.LabelFor(model => model.AccountNumber, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.AccountNumber)
@Html.ValidationMessageFor(model => model.AccountNumber)
</div>
</div>
}
  • @Html.LabelFor(model=>model.Account)产生Label
  • @Html.EditorFor(model=>model.Account)产生input
  • @Html.ValidationMessageFor(model=>model.AccountNumber)验证信息
  • @Html.ValidationSummary(true)验证汇总

2.4 修改CheckAccount

[Required]
//[StringLength(10, MinimumLength = 6)]
[RegularExpression(@"\d{6,10}", ErrorMessage = "账户必须是6-10个字符")]
[Display(Name = "账户")]
public string AccountNumber { get; set; } [Required]
public string FirstName { get; set; } [Required]
public string LastName { get; set; }
[Display(Name = "姓名")]
public string Name { get { return FirstName + " " + LastName; } } [Required]
[DataType(DataType.Currency)]
public int Balance { get; set; }

都是一些验证字段,注意每个都可以有ErrorMessage参数。

--

3. ViewModel

ViewModel不在数据库中存储,专门用于处理Form,类似Django中的Form类。

3.1 ViewModel定义

打开Models文件夹下的AccountViewModel,我们看到ViewModel定义与普通Model并没有明显区别,只是命名约定都是以"ViewModel"结尾。

public class LoginViewModel
{
[Required]
[Display(Name = "用户名")]
public string UserName { get; set; } [Required]
[DataType(DataType.Password)]
[Display(Name = "密码")]
public string Password { get; set; } [Display(Name = "记住我?")]
public bool RememberMe { get; set; }
}

3.2 ViewModel在Action中的应用

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
} // 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(model);
}

可以看到ViewModel只是接收用户的输入并封装,然后再使用封装的字段查找数据库中对应的Model。

3.3 ViewModel在视图中的应用

@model AspNetMVCEssential.Models.LoginViewModel

@{
ViewBag.Title = "登录";
} <h2>@ViewBag.Title。</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>使用本地帐户登录。</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.UserName)
</div>
</div>
...
}
...

ViewModel在视图中使用与普通Model并没有区别。


4. 数据库对象和Entity Framework

4.1 打开IdentityModels.cs文件

打开文件后,我们看到两个类:

  • ApplicationUser继承自IdentityUser,后者是用户类
  • ApplicationDbContext继承自IdentityDbContext<ApplicationUser>,后者又继承自DbContext,数据库上下文类,直接与数据库进行交互。

4.2 添加CheckoutAccount到数据库上下文

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
} public DbSet<CheckoutAccount> CheckoutAccounts { get; set; }
}

这样就可以操作数据库了。

4.3 添加Model外键

我们对CheckoutAccount添加如下两个属性:

public virtual ApplicationUser User { get; set; }
public string ApplicationUserId { get; set; }

其中的User使用virtual关键字是用来延迟加载的, 第二个属性用来指明外键,Entity Framework很聪明,能理解你的意思是指定它为外键。

4.4 在Action中使用

我们让用户在注册时系统自动生成一个CheckoutAccount记录:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//创建CheckoutAcount
var db = new ApplicationDbContext();
var checkoutAccount = new CheckoutAccount
{
AccountNumber = "00000123",
FirstName = model.FirstName,
LastName = model.LastName,
Balance = 0,
ApplicationUserId = user.Id
};
db.CheckoutAccounts.Add(checkoutAccount);
db.SaveChanges(); await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
} // 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(model);
}

运行,注册账户,我们会发现自动注册了CheckoutAccount对象,并且添加了外键。

4.5 Model在视图中的使用以及@Model关键字

之前使用ViewBag来向模板传递对象,ViewBag是动态类型的。ASP.NET MVC还提供传递强类型数据到模板的方法。强类型的好处是VS提供了智能提示。

比如之前我们使用了

return View(model);

检查对应的视图:

@model MvcMovie.Models.Movie

@{
ViewBag.Title = "Details";
} <h2>Details</h2> <div>
<h4>Movie</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Title)
</dt>
@*Markup omitted for clarity.*@
</dl>
</div>
<p>
@Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
@Html.ActionLink("Back to List", "Index")
</p>

我们看到在模板头部我们添加了@model语句,然后我们就可以在视图中使用Model对象来访问对应的对象及其属性。

对于列表型的数据,我们可以使用如下方法:

@model IEnumerable<MvcMovie.Models.Movie>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
</td>
</tr>
}

5. Entity Framework Code First迁移

现在我们想修改数据库,当然可以直接使用SQL Server修改,但也可以通过Code First迁移进行修改。

5.1 修改CheckoutAccount

[Required]
[StringLength(10)]
[Column(TypeName = "varchar")]
[RegularExpression(@"\d{6,10}", ErrorMessage = "账户必须是6-10个字符")]
[Display(Name = "账户")]
public string AccountNumber { get; set; }

5.2 打开迁移

打开NuPackage程序包管理器控制台。

PM> Enable-Migrations -ContextType ApplicationDbContext
正在检查上下文的目标是否为现有数据库...
检测到使用数据库初始值设定项创建的数据库。已搭建与现有数据库对应的迁移“201410311619291_InitialCreate”的基架。若要改用自动迁移,请删除 Migrations 文件夹并重新运行指定了 -EnableAutomaticMigrations 参数的 Enable-Migrations。
已为项目 AspNetMVCEssential 启用 Code First 迁移。

5.3 添加迁移

PM> Add-Migration AccountNumberChanges
正在为迁移“AccountNumberChanges”搭建基架。
此迁移文件的设计器代码包含当前 Code First 模型的快照。在下一次搭建迁移基架时,将使用此快照计算对模型的更改。如果对要包含在此迁移中的模型进行其他更改,则您可通过再次运行“Add-Migration AccountNumberChanges”重新搭建基架。

也可以生成迁移脚本:

PM> Update-Database -Script
正在应用显式迁移: [201410311643166_AccountNumberChanges]。
正在应用显式迁移: 201410311643166_AccountNumberChanges。

此时系统会自动弹出一个包含Script的迁移。

5.4 应用迁移

PM> Update-Database -Verbose
Using StartUp project 'AspNetMVCEssential'.
Using NuGet project 'AspNetMVCEssential'.
指定“-Verbose”标志以查看应用于目标数据库的 SQL 语句。
目标数据库为: “aspnet-AspNetMVCEssential-20141031091551”(DataSource: .,提供程序: System.Data.SqlClient,来源: Configuration)。
正在应用显式迁移: [201410311643166_AccountNumberChanges]。
正在应用显式迁移: 201410311643166_AccountNumberChanges。
...
正在运行 Seed 方法。

5.5 回滚迁移

PM> Update-Databse -TargetMigration IntialCreate

5.6 自动迁移

打开Migrations文件夹下的Configuration

internal sealed class Configuration : DbMigrationsConfiguration<AspNetMVCEssential.Models.ApplicationDbContext>
{
public Configuration()
{
//设置自动迁移
AutomaticMigrationsEnabled = true; ContextKey = "AspNetMVCEssential.Models.ApplicationDbContext";
}
//...
}

此后,发生变更时,直接使用:

PM> Update-Database -Verbose
Using StartUp project 'AspNetMVCEssential'.
Using NuGet project 'AspNetMVCEssential'.
指定“-Verbose”标志以查看应用于目标数据库的 SQL 语句。
目标数据库为: “aspnet-AspNetMVCEssential-20141031091551”(DataSource: .,提供程序: System.Data.SqlClient,来源: Configuration)。
没有挂起的显式迁移。
正在应用自动迁移: 201410311656382_AutomaticMigration。
System.Data.Entity.Migrations.Infrastructure.AutomaticDataLossException: 未应用自动迁移,因为自动迁移会导致数据丢失。如果要在可能导致数据丢失的情况下允许应用自动迁移,请在 DbMigrationsConfiguration 上将 AutomaticMigrationDataLossAllowed 设置为 "true"。也可以将 Update-Database 与 "-Force" 选项一起使用,或者构建基架执行显式迁移。
在 System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, VersionedModel sourceModel, VersionedModel targetModel, Boolean downgrading)
。。。
在 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
未应用自动迁移,因为自动迁移会导致数据丢失。如果要在可能导致数据丢失的情况下允许应用自动迁移,请在 DbMigrationsConfiguration 上将 AutomaticMigrationDataLossAllowed 设置为 "true"。也可以将 Update-Database 与 "-Force" 选项一起使用,或者构建基架执行显式迁移。

我们看到上面报了一些错误,这是因为可能会造成丢失,这就让我们可以强制更新:

PM> Update-Database -Verbose -Force

ASP.NET MVC5 学习笔记-3 Model的更多相关文章

  1. ASP.NET MVC5学习笔记01

    由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...

  2. ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器

    [TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...

  3. ASP.NET MVC5学习笔记之Filter提供体系

    前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的. 一.Filter集合 在ControlerActionI ...

  4. ASP.NET MVC5学习笔记之Controller同步执行架构分析

    在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...

  5. ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供

    一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...

  6. ASP.NET MVC5学习笔记之Filter基本介绍

    Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...

  7. ASP.NET MVC5 学习笔记-4 OWIN和Katana

    1. Owin OWIN全名:Open Web Interface for .NET. 它是一个说明,而非一个框架,该声明用来实现Web服务器和框架的松耦合.它提供了模块化.轻量级和便携的设计.类似N ...

  8. ASP.NET MVC5 学习笔记-2 Razor

    1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...

  9. Asp.net MVC5 学习笔记

    控制器(controller)主要负责响应用户的输入,并且在响应时修改模型(Model).通过这种方式,MVC模式中的控制器主要关注的是应用程序流.输入数据的处理,以及对相关视图(View)输出数据的 ...

随机推荐

  1. 学习使用crosswalk

    1.创建一个工程,最好使用Swift语言,或者也可以叫做雨燕语言,名字很好听吧,功能也是很强大的.言归正传,创建好工程后,需要用CocoaPod工具,不懂的童鞋自行google吧. 根据CrossWa ...

  2. android 回调

    调函数(callback Function),顾名思义,用于回调的函数.  回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数.回调函数是一个工作流的一部分,由工作流来决定函数的调用 ...

  3. HDU 4715 Difference Between Primes (打表)

    Difference Between Primes Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/O ...

  4. Extjs 4 生成饼状图的例子

    前台: //远程抄表设备下落图表数据 var Store1 = new Ext.data.Store({ <span style="white-space:pre"> ...

  5. 从零开始Unity3D游戏开发【2 简单的水管工例子】

    1.首先,创建一个新的Project. 2.hierarchy(层)窗体下的Create下添加一个plane(平面) 3.调整Main Camera的视角,让panel显示在Game窗体.这一步比较困 ...

  6. js中的数组和字符串的一些方法

    数组的一些方法: 1.join()和split()方法 <script type="text/javascript">var x;var a=new Array();a ...

  7. select2插件常用方法汇总

    1.获取下拉框的value和text <input type="hidden" name="xa" id="xa" data-plac ...

  8. Java动态编程---动态代理

    java中动态编程用到的技术有:反射(动态代理),javassist和ASM,这几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成 ...

  9. json中头疼的null

    在服务器返回 json 数据的时候,时常会出现如下数据 "somevalue":null 这个时候,json 解析的时候,就会吧这个 null 解析成 NSNull 的对象,我们向 ...

  10. 转 --maven系列之二 安装与配置

    http://blog.csdn.net/jiuqiyuliang/article/details/45390313 [项目管理和构建]——Maven下载.安装和配置(二) 标签: 工具开发maven ...