ASP.NET MVC5 学习笔记-3 Model
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的更多相关文章
- ASP.NET MVC5学习笔记01
由于之前在项目中也使用MVC进行开发,但是具体是那个版本就不是很清楚了,但是我觉得大体的思想是相同的,只是版本高的在版本低的基础上增加了一些更加方便操作的东西.下面是我学习ASP.NET MVC5高级 ...
- ASP.NET MVC5 学习笔记-1 控制器、路由、返回类型、选择器、过滤器
[TOC] 1. Action 1.1 新建项目 新建项目->Web->Asp.net Web应用程序,选择MVC,选择添加测试. 在解决方案上右键,选择"管理NuGet程序包& ...
- ASP.NET MVC5学习笔记之Filter提供体系
前面我们介绍了Filter的基本使用,但各种Filter要在合适的时机运行起来,需要预先准备好,现在看看ASP.NET MVC框架是怎么做的. 一.Filter集合 在ControlerActionI ...
- ASP.NET MVC5学习笔记之Controller同步执行架构分析
在开始之前,声明一下,由于ASP.NET MVC5正式发布了,后面的分析将基于ASP.NET MVC5最新的源代码.在前面的内容我们分析了怎样根据路由信息来确定Controller的类型,并最终生成C ...
- ASP.NET MVC5学习笔记之Action参数模型绑定之模型元数据和元数据提供
一. 元数据描述类型ModelMetadata 模型元数据是对Model的描述信息,在ASP.NET MVC框架中有非常重要的作用,在模型绑定,模型验证,模型呈现等许多地方都有它的身影.描述Model ...
- ASP.NET MVC5学习笔记之Filter基本介绍
Filter是ASP.NET MVC框架提供的基于AOP(面向方面)设计,提供在Action执行前后做一些非业务逻辑通用处理,如用户验证,缓存等.现在来看看Filter相关的一些类型信息. 一.基本类 ...
- ASP.NET MVC5 学习笔记-4 OWIN和Katana
1. Owin OWIN全名:Open Web Interface for .NET. 它是一个说明,而非一个框架,该声明用来实现Web服务器和框架的松耦合.它提供了模块化.轻量级和便携的设计.类似N ...
- ASP.NET MVC5 学习笔记-2 Razor
1. Razor @*注释*@ 你在用 @Request.Browser.Browser, 发送邮件给support@qq.com, 转义@@qq @{ var amounts = new List& ...
- Asp.net MVC5 学习笔记
控制器(controller)主要负责响应用户的输入,并且在响应时修改模型(Model).通过这种方式,MVC模式中的控制器主要关注的是应用程序流.输入数据的处理,以及对相关视图(View)输出数据的 ...
随机推荐
- JavaSE思维导图(六)
- SQL Server 2012学习笔记 2 Server Core中命令行安装SQL
Setup.exe /qs /ACTION=Install /FEATURES=SQLEngine,Replication /INSTANCENAME=MSSQLSERVER /SQLSVCACCOU ...
- html5 meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-sca ...
- T4模板之菜菜鸟篇
一.废话 T4(Text Template Transformation Toolkit)是微软官方在VisualStudio 2008中开始使用的代码生成引擎.在 Visual Studio 中,“ ...
- 读书笔记 - 设计模式(Head First)
设计模式让你和其他开发人员之间有共享的词汇,设计模式可以把你的思考架构的层次提高到模式层面,而不是停留在琐碎的对象上. 设计原则: 封装变化:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需 ...
- mysql的触发器
删除触发器 drop TRIGGER 触发器名字; 查找库里面的所有触发器 SELECT * FROM information_schema.`TRIGGERS`;show triggers 触发器语 ...
- [C#]中英文字幕合并的小程序
今天班里小组合作录了一个视频,我给它做了字幕的时间轴.为了让这个视频假装很高端的样子,我想再加上英文的字幕.中文的纯字幕文本先搞成一句一行,然后放到Google翻译上,复制英文保存在Eng.txt. ...
- JS 节流阀
JS 节流阀 参考 https://github.com/hahnzhu/read-code-per-day/issues/5 节流阀 节流阀的基本原理 事件函数的执行都记下当前时间, 只有当前时间与 ...
- html 转 js 字符串
看到一个牛人的博客 http://riny.net/lab/#tools_html2js 看了下他的代码 挺棒的 所依赖的两个库在这里 https://github.com/Bubblings/l ...
- ONFI闪存数据通道接口标准
早期的闪存产品每个厂家的设计标准各有不同,会碰到各种各样的问题,特别是到了06年之后,闪存产业市场需求开始发力,造成了迫切需要一个统一的标准来改变这个问题. 2007年1月,以英特尔,镁光,海力士,意 ...