ASP.NET MVC5+EF6(入门二数据注解)

目录

【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)
【第二篇】ASP.NET MVC快速入门之数据注解(MVC5+EF6)
【第三篇】ASP.NET MVC快速入门之安全策略(MVC5+EF6)
【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)

一、数据库连接字符串

上一篇文章中,我们使用MVC的模板自动生成了CRUD的全部操作,但是没有配置数据库连接字符串,那么数据存到什么地方了?
打开项目的App_Data目录,你可以发现数据库原来在这里:


我们通过VS自带的数据库访问工具,来看下表结构和其中的数据,首先找到[服务器资源管理器]面板,新增数据库连接:


在添加连接向导对话框中,输入服务器名:(LocalDb)\MSSQLLocalDB,这个是VS2015自带的LocalDb的服务器实例名称(如果你使用VS2013,这个名称可能是:(LocalDB)\v11.0)。数据库选择我们刚刚创建的StudentDbContext。

原来如果没有显式的指定数据库连接字符串,VS会使用默认的LocalDb实例,这个对应关系在Web.config中有定义:

<entityFramework>
       <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
         <parameters>
              <parameter value="mssqllocaldb" />
         </parameters>
       </defaultConnectionFactory>
       <providers>
         <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
       </providers>
</entityFramework>

当然我们也可以明确指定数据库连接字符串:

<connectionStrings>
       <add name="DefaultConnection"
connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\AspNetMvc.QuickStart.Models.StudentDbContext.mdf;Initial Catalog=AspNetMvc.QuickStart.Models.StudentDbContext;Integrated Security=True"
         providerName="System.Data.SqlClient" />
</connectionStrings>

然后在代码中引用这个数据库连接字符串:

public class StudentDbContext : DbContext
{
       public StudentDbContext() : base("DefaultConnection")
       {
       }
       public DbSet<Student> Students { get; set; }
}

注意:如果使用的VS2013,Data Source应该是(LocalDb)\v11.0,而VS2015对应的是(LocalDb)\MSSQLLocalDB。
经过这个改变,在真正部署到MSSQL服务器时,简单修改数据库连接字符串就可以了。

数据库表结构

打开Students的表定义:


可以看下EF是怎么将Student模型映射到数据库表结构的:

  1. 模型中ID属性的数据映射为表的主键。
  2. 模型中的string类型映射为表的nvarchar(MAX)。
  3. 模型中的int和DateTime分别映射为表的int和datetime类型。

再来看下上一篇文章中添加到表中的数据:

如果你之前有数据库设计的经验,会很容易发现这个表结构的问题:

  1. Name和Major存储字符串,一般需要限制最大长度,比如nvarchar(200)。
  2. Name和Major列应该不允许为空。

那么怎么来实现这两个需求呢?直接修改数据库肯定是不行的!

二、数据注解

我们应该从模型入手,还记得我们在上一篇文章结尾说的那句话吗,数据模型不仅会影响数据库的表结构,还会控制MVC视图层的客户端验证和控制器层的服务器端验证。
修改Student模型类,添加适当的数据注解:

public class Student
{
       public int ID { get; set; }

       [Required]
       [StringLength(200)]
       public string Name { get; set; } 

       public int Gender { get; set; }

       [Required]
       [StringLength(200)]
       public string Major { get; set; }
       public DateTime EntranceDate { get; set; }
}

如果输入[Required]时没有智能感知,很可能是没有引用相应的命名空间,VS可以很方便的协助我们添加:



这样就会在文件头部添加如下引用:

    using System.ComponentModel.DataAnnotations;

直接运行项目(Ctrl+F5),此时我们会看到如下的错误页面:

相信使用EF的同学都会遇到这个页面,上面的提示也很明确,包含两个层次的信息:

  1. 数据库创建之后模型改变了。
  2. 可以使用数据迁移来更新数据库。

三、数据迁移(Migrations)

从VS的[工具]菜单中,找到Nuget包管理器控制台:


启用数据迁移

在控制台中输入如下命令:Enable-Migrations

这时会在项目目录中增加一个Migrations文件夹,里面放置了两个文件:

EF会通过C#代码的方式将每一次对模型的修改保存到这个文件夹中,现在来看下生成的文件内容:


每个迁移文件,都包含Up和Down两个重写函数,分别对应于更新和回退。上面的代码也很直白,Up函数中创建一个Students表,定义表结构并指定ID主键(PrimaryKey),Down函数用来回退操作,里面简单的删除了Students表。

可以看到,这里的创建表操作并没有使用最新的模型(Name列没有nullable的设置),因为这是初始模型对应的表结构,EF会在数据库中自动生成一个名为__MigrationHistory表来跟踪数据库的状态。

PS.
如果项目中有多个上下文则需要明确指是哪一个,不然就会报错。如下:
More than one context type was found in the assembly '解决方案名称'.
解决方案:
修改命令为:Enable-Migrations -ContextTypeName 上下文完整路径
如:
Enable-Migrations -ContextTypeName Appointment.Models.InfoDBContext

增加迁移项

增加迁移项需要我们手工来进行,在程序包管理器控制台中,输入如下命令:
Add-Migration 迁移名称,如:Add-Migration Add_Annotation_Name_Major

这时会在Migrations目录下生成迁移文件,文件是以[时间+迁移名]命名的,方便查找:
201905141317186_Add_Annotation_Name_Major.cs

更新到数据库

此时,数据库尚未改变,我们还需要手工命令来更新数据库:Update-Database

此时,再来查看数据库中Students的表结构:


Name列的数据类型和是否允许Null都已经改变了。

在真实的项目中,数据库可能部署在远程服务器中,这时我们就不能直接在VS中通过Update-Database来更新数据库了。
不过我们可以生成更新SQL脚本,然后拿到数据库服务器上执行。生成这个SQL脚本的方法:
Update-Database -Script -SourceMigration: InitialCreate -TargetMigration: Add_Annotation_Name_Major


来看下生成的SQL更新脚本:

有了这个SQL更新脚本,我们就可以方便的更新远程数据库了。

四、视图的客户端验证

现在运行项目,转到创建页面:


可以看到,如果Name为空则会有错误提示信息,而Major输入字符串过多,也会有提示信息,而这些设置是来自模型的数据注解。

如果你查看页面源代码,会发现Major输入框的input标签上有相应的自定义属性data-val-length-max=200data-val-length,而这些属性值正是来自于模型的数据注解。

五、控制器的服务器端验证

在启用JavaScript的情况下,由于所有的错误输入在客户端就会被拦截,所以根本到达不了服务器,不过这并不表示恶意用户无法提交错误的输入,有很多种方法可以做到:

禁用JavaScript

不同浏览器禁用JavaScript的方法不同,在Chrome中,F12打开开发工具,然后找到设置对话框:


此时提交页面,你会看到和前面完全相同的页面,由于本地运行速度很快,你甚至可能没意识已经发起了一次HTTP POST请求,而显示错误提示的页面来自服务器,而不是客户端:

响应正文包含如下内容,其中错误信息是服务器端生成的:

<div class="form-group">
       <label class="control-label col-md-2" for="Name">Name</label>
       <div class="col-md-10">
              <input class="input-validation-error form-control text-box single-line" data-val="true" data-val-length="字段 Name 必须是最大长度为 200 的字符串。" data-val-length-max="200" data-val-required="Name 字段是必需的。" id="Name" name="Name" type="text" value="" />
              <span class="field-validation-error text-danger" data-valmsg-for="Name" data-valmsg-replace="true">Name 字段是必需的。</span>
       </div>
</div>

此时再回过头来看下Students控制器中Create操作方法的定义:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student)
{
       if (ModelState.IsValid)
       {
              db.Students.Add(student);
              db.SaveChanges();
              return RedirectToAction("Index");
       } 
       return View(student);
}

如果验证失败,则不更新数据库,并返回带Student模型的视图。

模拟POST请求

有很多工具可以模拟POST,这里我们讲解如果使用Fiddler来模拟项服务器提交POST请求。

打开Fiddler,就开始自动监测所有的HTTP请求,这时我们刷新Create页面,并在JavaScript禁用的情况下,提交表单,这时会有两个请求:

首先选中左侧的第二个请求,右侧面板中选择Inspectors->WebForms,下面会显示三个窗格:

  1. QueryString:当前请求的URL查询字符串。
  2. Body:POST请求的表单参数。
  3. 第三部分:响应正文,我们可以看到服务器端返回的错误信息。

现在切换到Composer选项卡,我们可以在这里面模拟POST请求:

上面有一段提示信息:使用这个页面创建一个请求。你可以通过拖拽的方式从左侧会话列表中拷贝一个之前的请求。
这就方便多了,我们从左侧选中第二个请求并拖拽到本页面:

这时页面背景变成明显的绿色以作提示,拖拽结束:

这时,Fiddler自动帮我们设置了模拟POST请求的参数,拷贝自之前的某个请求,这时的[Request Body]是经过URL编码的,我们可以方便的进行解码:

我们把这段代码修改成:

__RequestVerificationToken=wwoxICDootbixw8YMiFIOU1WW95QSCicREsWLeewlSAE28sdyEA0ZChlY0nfuOlxu2WDIjcrx086GYkaBOAtewyARWbeRZp0kD6tRt-hyAs1&Name=张三石&Gender=1&Major=&EntranceDate=2000-09-01

把这段字符串拷贝到Fiddler中的[Request Body],并点击[Execute]按钮,这时会发起一个新的模拟请求:

注意这个请求并不是从页面发出的,而是通过工具模拟的HTTP POST请求,并且我们还修改了其中的表单参数(Name=张三石,Major=空字符串),这样当然也就会躲开浏览器端的JavaScript验证规则,但是还是无法穿透服务器端的验证。

这也正是MVC中数据注解带来的便利,一个地方定义,三个地方使用(数据库表结构、客户端验证,服务器端验证)。

六、小结

本章我们首先查看了EF自动生成的数据库结构,然后为数据模型添加数据注解,继而介绍了数据迁移的工作过程。数据注解不仅对数据库表结构产生影响,而且会应用到前台的客户端验证和服务器端验证,接下来我们详细讲解了两则躲避客户端验证的手段,分别为禁用JavaScript和模拟POST请求。从而更加深刻的认识到数据注解给我们提供的便利:一个地方定义,三个地方使用(数据库表结构、客户端验证,服务器端验证)

在创建新用户页面,我们可以看到两个安全相关的代码(ValidateAntiForgeryToken和Bind),它们分别用于阻止CSRF跨站请求伪造和Over-Posting过多提交攻击,下一篇文章我们会详细介绍。

下载示例源代码

文章转载自:http://www.cnblogs.com/sanshi/p/6211164.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容