ASP.NET MVC with Entity Framework and CSS一写翻体系作品的第四节:更尖端的数额管理

  于即刻同一节大家以学习怎么着对地去分类信息,咋样为数据库填充种子数据,如何利用Code
First
Migrations基于代码更改来更新数据库,然后上咋样举办带有自定义错误音信之表达。

注意:要是您想循本章的代码编写示例,你要就第三节要直接从www.apress.com下载第三章节的源代码。

4.1 删除作为一个外键的实体

  到近日截至,我们都成功了向阳站点添加搜索和过滤的功用,并且我们早已得以通往站点添加一些分类及制品消息。上面我们将考虑当尝试去实体音讯平时会生出啊工作。

  首先,向站点添加一个称为吧Test的初分类,然后再度续加一个号称吧Test的活,并拿拖欠产品之归类指定为分类Test。现在,大家以分类的目录(Index)页面删除Test分类,然后交由删除操作,这时,站点将碰面丢掉来一个错误,如图4-1所著。

Bootstrap 1

图4-1:当试着抹一个分类时,发生的一个参照完整性错误

  之所以会发出这似是而非,是坐Products表中的CategoryID列是一个外键列,该列引用Categories表中之ID列。当一个分拣为删除时,Products表没有爆发变更,导致Products表中之categoryID外键列引用Categories表中一个就给去除的归类ID,这虽会促成一个谬误爆发。

  为理解决是题材,我们需要更正由基架生成的代码,以用为影响之Products表中的外键列的价值设置也null。修改\Controllers\CategoriesController.cs文件中之Delete方法(HttpPost版本)以符合下边列有之粗体代码:

 1 // POST: Categories/Delete/5
 2 [HttpPost, ActionName("Delete")]
 3 [ValidateAntiForgeryToken]
 4 public ActionResult DeleteConfirmed(int id)
 5 {
 6     Category category = db.Categories.Find(id);
 7 
 8     foreach(var p in category.Products)
 9     {
10         p.CategoryID = null;
11     }
12 
13     db.Categories.Remove(category);
14     db.SaveChanges();
15     return RedirectToAction("Index");
16 }

  那段代码添加了一个简易的foreach循环总体历Category实体的Products导航属性,然后以各类一个Product对象的CategoryID属性值设置也null。现在,大家再度品尝在抹Test分类,该分类将相会受删,并且不相会来错误,同时数据库中之Products表的Test产品之CategoryID类会被设置为null。

4.2 启用Code First
Migrations数据库迁移以及朝数据库填充种子数据

  时,我们都是以站点中手动输入数据来创建分类与成品音讯。在出环境面临,对于测试一个初的效率,这是如出一辙种植相比好的法子,可是,假如我们回忆当另外环境被不过靠地、轻松地重建相同之数码应怎么处置呢?这便待Entity
Framework的一个称播种(seeding)的特色来发挥效率了。播种(seeding)被用于因编程的方于数据库被开创实体,并且可以操纵特定条件下之输入。

  大家现固然从头上学咋样行使称之为Code First
Migrations的性状来为数据库填充种子数据。迁移(Migrations)是基于对模型类的代码的改来更新数据库架构的同样种植艺术。从兹开,本书从始至终都相会使用迁移(Migrations)的法门来更新数据库架构。

  首先,我们要立异web.config文件中之数据库连字符串,以便创立一个用来测试种子数据是否科学工作之初数据库。更新StoreContext的connectionString属性如下面的代码所示为创办一个叫也BabyStore2.mdf底新数据库。

1 <connectionStrings>
2   <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\aspnet-BabyStore-20161229112118.mdf;Initial Catalog=aspnet-BabyStore-20161229112118;Integrated Security=True"
3     providerName="System.Data.SqlClient" />
4   <add name="StoreContext" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\BabyStore2.mdf;Initial Catalog=BabyStore2;Integrated Security=True"
5     providerName="System.Data.SqlClient" />
6 </connectionStrings>

  即使以本书中connectionString彰显也多执行,但保险于Visual
Studio中维系它吗平实践代码。

4.2.1 启用Code First Migration

  于主菜单栏中选用【视图】->【其他窗口】->【程序包管理器控制高】打开程序包管理器控制高窗口。在斯窗口中好输入使用迁移(Migrations)的有命令。大家要召开的首先宗工作就是是针对性要履更新的数据库的上下文启动搬迁(Migrations)操作。要是只出一个上下文,则达到下文可选。

  于立刻无异节中,大家感谢兴趣之是活及分类数据,由此,在程序包管理器控制麦德林输入下列命令:

1 Enable-Migrations -ContextTypeName BabyStore.DAL.StoreContext

  就算大家对地履行完毕命令,Visual Studio应该要图4-2所出示。

Bootstrap 2

图4-2:启用Code First Migrations

  启用迁移(Migrations)的以在列根本目录下也填补加了一个名为也Migrations的文书夹,在其中含一个叫做吧Configuration.cs的公文,该公文用于配置迁移(Migrations)。图4-3形精通决方案资源管理器中之新建的文本夹和文件。

Bootstrap 3

图4-3:当启用迁移(Migrations)的早晚所创的Migrations文件夹和Configuratiion文件

  下一致步,我们于先后包管理器控制夏洛蒂输入以下命令以补加一个称之为InitialDatabase的开迁移(initial
migration)。

1 add-migration InitialDatabase

  该令在Migrations文件夹下创办了一个文本名格式为<TIMESTAMP>_InitialDatabase.cs的新文件,<TIMESTAMP>表示创立该文件时的辰。Up方法用于成立数量库表,Down方法用于去其。在这一个新文件被所富含的代码如下所示。大家得望Up方法包含重建Categories和Products表的片段代码(伴随着数据类型和键的重建)。

 1 namespace BabyStore.Migrations
 2 {
 3     using System;
 4     using System.Data.Entity.Migrations;
 5 
 6     public partial class InitialDatabase : DbMigration
 7     {
 8         public override void Up()
 9         {
10             CreateTable(
11                 "dbo.Categories",
12                 c => new
13                 {
14                     ID = c.Int(nullable: false, identity: true),
15                     Name = c.String(),
16                 })
17                 .PrimaryKey(t => t.ID);
18 
19             CreateTable(
20                 "dbo.Products",
21                 c => new
22                 {
23                     ID = c.Int(nullable: false, identity: true),
24                     Name = c.String(),
25                     Description = c.String(),
26                     Price = c.Decimal(nullable: false, precision: 18, scale: 2),
27                     CategoryID = c.Int(),
28                 })
29                 .PrimaryKey(t => t.ID)
30                 .ForeignKey("dbo.Categories", t => t.CategoryID)
31                 .Index(t => t.CategoryID);
32 
33         }
34 
35         public override void Down()
36         {
37             DropForeignKey("dbo.Products", "CategoryID", "dbo.Categories");
38             DropIndex("dbo.Products", new[] { "CategoryID" });
39             DropTable("dbo.Products");
40             DropTable("dbo.Categories");
41         }
42     }
43 }

  这段代码不纵即会面用来创设一个新的数据库,在这前边,我们得改进Migrations\Configuratiion.cs文件中之Seed()方法为向数据库添加一些测试数据。

4.2.2 向数据库填充种子数据

  当用Code First
Migrations时,Seed方法用于向数据库添加测试数据。一般景色下,只有当数据库让创设时要向该法被补充加新的数时,这多少个测试数据才会合让补充加到数据库被。当数据模型被移时,数据未会面丢掉。当迁移到变化环境时,大家要确定如何数据是始于数据要未是测试数据,以及愈发恰当地更新Seed方法。

  为了拿新的种和活数据增长到数据库被,大家需要革新Migrations\Configurations.cs文件被之Seed方法,Seed方法修改后的代码如下所示:

 1 namespace BabyStore.Migrations
 2 {
 3     using System.Data.Entity.Migrations;
 4     using System.Linq;
 5     using Models;
 6     using System.Collections.Generic;
 7 
 8     internal sealed class Configuration : DbMigrationsConfiguration<BabyStore.DAL.StoreContext>
 9     {
10         public Configuration()
11         {
12             AutomaticMigrationsEnabled = false;
13         }
14 
15         protected override void Seed(BabyStore.DAL.StoreContext context)
16         {
17             var categories = new List<Category>
18             {
19                 new Category { Name = "Clothes" },
20                 new Category { Name = "Play and Toys" },
21                 new Category { Name = "Feeding" },
22                 new Category { Name = "Medicine" },
23                 new Category { Name = "Travel" },
24                 new Category { Name = "Sleeping" }
25             };
26 
27             categories.ForEach(c => context.Categories.AddOrUpdate(p => p.Name, c));
28             context.SaveChanges();
29 
30             var products = new List<Product>
31             {
32                 new Product { Name = "Sleep Suit", Description = "For sleeping or general wear", Price = 4.99M, CategoryID = categories.Single( c => c.Name == "Clothes").ID },
33                 new Product { Name = "Vest", Description = "For sleeping or general wear", Price = 2.99M, CategoryID = categories.Single( c => c.Name == "Clothes").ID },
34                 new Product { Name = "Orange and Yellow Lion", Description = "Makes a squeaking noise", Price = 1.99M, CategoryID = categories.Single( c => c.Name =="Play and Toys").ID },
35                 new Product { Name = "Blue Rabbit", Description = "Baby comforter", Price = 2.99M, CategoryID = categories.Single( c => c.Name == "Play and Toys").ID },
36                 new Product { Name = "3 Pack of Bottles", Description = "For a leak free drink everytime", Price = 24.99M, CategoryID = categories.Single( c => c.Name == "Feeding").ID },
37                 new Product { Name = "3 Pack of Bibs", Description = "Keep your baby dry when feeding", Price = 8.99M, CategoryID = categories.Single( c => c.Name == "Feeding").ID },
38                 new Product { Name = "Powdered Baby Milk", Description = "Nutritional and Tasty", Price = 9.99M, CategoryID = categories.Single( c => c.Name == "Feeding").ID },
39                 new Product { Name = "Pack of 70 Disposable Nappies", Description = "Dry and secure nappies with snug fit", Price = 19.99M, CategoryID = categories.Single( c => c.Name == "Feeding").ID },
40                 new Product { Name = "Colic Medicine", Description = "For helping with baby colic pains", Price = 4.99M, CategoryID = categories.Single( c => c.Name == "Medicine").ID },
41                 new Product { Name = "Reflux Medicine", Description = "Helps to prevent milk regurgitation and sickness", Price  =4.99M, CategoryID = categories.Single( c => c.Name == "Medicine").ID },
42                 new Product { Name = "Black Pram and Pushchair System", Description = "Convert from pram to pushchair, with raincover", Price = 299.99M, CategoryID = categories.Single( c => c.Name == "Travel").ID },
43                 new Product { Name = "Car Seat", Description="For safe car travel", Price = 49.99M, CategoryID = categories.Single( c => c.Name == "Travel").ID },
44                 new Product { Name = "Moses Basket", Description = "Plastic moses basket", Price = 75.99M, CategoryID = categories.Single( c => c.Name == "Sleeping").ID },
45                 new Product { Name = "Crib", Description = "Wooden crib", Price = 35.99M, CategoryID = categories.Single( c => c.Name == "Sleeping").ID },
46                 new Product { Name = "Cot Bed", Description = "Converts from cot into bed for older children", Price = 149.99M, CategoryID = categories.Single( c => c.Name == "Sleeping").ID },
47                 new Product { Name = "Circus Crib Bale", Description = "Contains sheet, duvet and bumper", Price = 29.99M, CategoryID = categories.Single( c => c.Name == "Sleeping").ID },
48                 new Product { Name = "Loved Crib Bale", Description = "Contains sheet, duvet and bumper", Price = 35.99M, CategoryID = categories.Single( c => c.Name == "Sleeping").ID }
49             };
50 
51             products.ForEach(c => context.Products.AddOrUpdate(p => p.Name, c));
52             context.SaveChanges();
53         }
54     }
55 }

  这段代码分别成立了一个分类与成品对象的列表,并拿其保存及数据库被。为了讲其行事原理,我们将分析类有的代码。首先,分别创制了一个称为也categories的变量和一个分类目的的列表,并以分类目标的列表赋值给categories变量,具体代码如下所示:

var categories = new List<Category>
{
  new Category { Name=”Clothes” },
  new Category { Name=”Play and Toys” },
  new Category { Name=”Feeding” },
  new Category { Name=”Medicine” },
  new Category { Name=”Travel” },
  new Category { Name=”Sleeping” }
};

  下一行代码是categories.ForEach(c =>
context.Categories.AddOrUpdate(p => p.Name,
c));,如果以数据库被未在同名的归类音信,这行代码将会见补充加相同漫长分类信息,否则将会师更新她。在这么些事例中,我们要是分类的称呼都是唯一的。

  最终一片段代码是context.SaveChanges();当调用该格局时,会以转保存至数据库被。在这文件中,大家调整用该法简单破,但就不是得的,我们可只调用它同潮。可是,假如在写入数据库时暴发一个错,每保存一个实体类型之后都调用它一律不良,可以帮忙大家永恒出问题之源代码。

  假诺大家相遇这样一个景观,我们得采用非凡相似的数量向数据库添加六只实体对象(比如,六只同名的分类),大家好逐个添加到上下文中,如下代码所示:

1 context.Categories.Add(new Category { Name = "Clothes" });
2 context.SaveChanges();
3 context.Categories.Add(new Category { Name = "Clothes" });
4 context.SaveChanges();

  再一次重申,上述代码没有必要多次调用SaveChanges方法保存改动,不过这么做有益援救我们识破有误的源代码。添加出品音信之代码有与长分类信息的代码有同等的情势,除了大家采用下列代码来因分类实体生成产品音讯中之CategoryID字段:CategoryID
= categories.Single(c => c.Name == “Clothes”).ID。

4.2.3
使用起来数据库迁移创造数据库

  现在我们准备利用自于Seed方法中的测试数据来成立一个初的数据库。在次包管理器控制夏洛特(Charlotte),运行如下命令:

1 update-database

  要是工作健康,大家当于喻正在利用迁移,并且正在运作Seed方法,如图4-4所出示。

Bootstrap 4

图4-4:动update-database命令成功更新数据库后的输出

  命名也BabyStore2.mdf底初数据库让成立以列根本目录下的App_Data文件夹着。当运行update-database命令时,Migrations\Configuration.cs文件被之Up方法吃调用,该措施用于创立数据库被的申。要是要翻该数据库,我们得以打开SQL
Server对象资源管理器,然后导航及BabyStore2.mdf数据库。假设大家都开辟了SQL
Server对象资源管理器,可能要点击刷新按钮才会显示有新创造的数据库。右键点击Products表,然后由菜单中选用【查看数据】选项来查看表中数量。图4-5体现了Products表中之数。这一个数量在Seed方法运行时为成立。

Bootstrap 5

图4-5:运转Seed方法所制造的Products表的多寡

  提示:而我们在App_Data文件夹着扣无至外东西,则在化解方案资源管理器中点击“显示所有文件”按钮即可。

  不调试运行站点,大家现在会看到由Seed方法所生存的新的分类(如图4-6)和活(如图4-7)消息。

Bootstrap 6

图4-6:分类索引(Index)页所著的由Seed方法变的数据

Bootstrap 7

图4-7:活目录(Index)页所凸显的由Seed方法变的数目

4.3
添加多少申明和格式化模型类之间的牢笼

  截至到目前停止,在此站点中所录入的数额还是坐某种格式彰显的多少(比如,货币)都未曾开展求证。举个例子,我们全然好输入一个名称为23底分类名。又或者,一个用户可不输入分类名,提交后为会面保留及数据库中,然而以展现分类的目录(Index)页面时,应用程序会遗弃来一个荒唐。

  删除我们正好创设的分类名为23的归类。假使我们为创立了一个分类名为空的分类,我们于SQL
Server对象资源管理器中呢将这分类删除。

  于第2回,我们读了怎么着采纳MetaDataType类来诠释一个曾是的类似,而无是平昔当此类中拔取注明。不过,在本书剩余章节,为了简单起见,我们直接在近似本身进行改动。

4.3.1
向Category类添加证和格式

  大家打算于分类信息充裕如下验证和格式:

  • 名字段不可知为空
  • 号字段只能接受字母
  • 称字段的尺寸在3-50独字母里

  为了形成上述目的,大家亟须修改Models\Category.cs文件,代码如下所示:

 1 using System.Collections.Generic;
 2 using System.ComponentModel.DataAnnotations;
 3 
 4 namespace BabyStore.Models
 5 {
 6     public class Category
 7     {
 8         public int ID { get; set; }
 9 
10         [Required]
11         [StringLength(50, MinimumLength = 3)]
12         [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
13         public string Name { get; set; }
14         public virtual ICollection<Product> Products { get; set; }
15     }
16 }

  [Required]特征使得该属性是得的,也就是说,它不可知为null或空。而[StringLength(50,
MinimumLength =
3)]特点指示以此字段中输入的字符串的尺寸要重新3及50个字符中。最终一个特性[RegularExpression(@”^[A-Z]+[a-zA-Z”-‘\s]*$”)]使用一个正则说明式提示该字段只好分包字母和空格,还得坐分外写字母先河。简单来讲,这些表达式的意就是只可以以至极写字母先导,后及两只假名或空格。在本书中,我们不会合含有正则表明式的知,因为在网上发差不五只指点手册可用。假使我们惦记深切摸底正则表明式的连锁文化,可以试试着下站点https://regex101.com/

  下一样步,我们不调试启动站点,然后点击分类链接。图4-8来得结果页面。我们会合见到莫亮分类索引(Index)页面,而是体现了一个错误信息,该错误信息提醒我们StoreContext上下文的模子在数据库创立好后已发出变更。

Bootstrap 8

图4-8:当型与数据库不同台时之消息展现

  造成这同题目之缘故是因Category类现在曾起变更,它需要以此更改使用到数据库中。我们抬高到名称列的老三个性状中之有限单特征需要使用到数据库被。这将保证该列不克啊空,并且针对该列应用maxLength属性。正则表达式和太小尺寸不适用于数据库。

  为了解决那题目,打开程序包管理器控制台,使用下列命令添加一个新的搬迁(Migration):

1 add-migration CategoryNameValidation

  新的动迁文件将汇合吃创建,该文件所涵盖的代码用于革新Categories表中之Name列。

 1 namespace BabyStore.Migrations
 2 {
 3     using System;
 4     using System.Data.Entity.Migrations;
 5 
 6     public partial class CategoryNameValidation : DbMigration
 7     {
 8         public override void Up()
 9         {
10             AlterColumn("dbo.Categories", "Name", c => c.String(nullable: false, maxLength: 50));
11         }
12 
13         public override void Down()
14         {
15             AlterColumn("dbo.Categories", "Name", c => c.String());
16         }
17     }
18 }

  为了将移应用叫数据库,在次包管理器控制夏洛特(Charlotte)运作下列命令:

1 update-database

  现在,数据库被的Categories表中之Name列将会给更新,如图4-9所出示。注意丰富允许为空的复选框现在从不被勾选,并且数据类型变成了nvarchar(50)。

Bootstrap 9

图4-9:更新Name列的Categories表和T-SQL脚本

  现在再也启航站点,点击主页上之分类链接,然后点击Create
New链接。现在尝试着充分一个号为空的归类,这多少个时节站点会打招呼我们顿时是免容许的,如图4-10所著。

Bootstrap 10

图4-10:当尝试添加一个空的归类时会晤来得一个错误消息

  现在我们再品尝创制一个称呼也Clothes
2的归类。图4-11显得任何一个消息指示。

Bootstrap 11

图4-11:品以分拣名被输入数字时所体现的指示信息

  就像大家所见到的这样,图4-11所出示的新闻是用户不友好的。幸运的是,ASP.NET
MVC允许大家重写错误新闻的文本,只需要在特点中输入一个非凡的参数即可。为了充足更加和睦的谬误指示,修改\Models\Category.cs文件也下列所出示之代码:

 1 using System.Collections.Generic;
 2 using System.ComponentModel.DataAnnotations;
 3 
 4 namespace BabyStore.Models
 5 {
 6     public class Category
 7     {
 8         public int ID { get; set; }
 9 
10         [Required(ErrorMessage = "分类名称不能为空!")]
11         [StringLength(50, MinimumLength = 3, ErrorMessage = "请确保输入的分类名称的长度在3~50个字符之间!")]
12         [RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$", ErrorMessage = "请确保输入的分类名字以大写字母开头,后面只能是字母或空白!")]
13         public string Name { get; set; }
14         public virtual ICollection<Product> Products { get; set; }
15     }
16 }

  现在莫调试启动站点,再一次创设一个曰也Clothes
2的归类,这一次所出示的错误信息更加有义,如图4-12所体现。

Bootstrap 12

图4-12:从定义错误验证音信

4.3.2 向Product类添加格式和表明

  Product类所含有的性能需要更为扑朔迷离的特性,比如格式化货币、多行展现等。修改Models\Product.cs文件为下列所著之代码:

 1 using System.ComponentModel.DataAnnotations;
 2 
 3 namespace BabyStore.Models
 4 {
 5     public partial class Product
 6     {
 7         public int ID { get; set; }
 8         [Required(ErrorMessage = "产品名称不能为空!")]
 9         [StringLength(50, MinimumLength = 3, ErrorMessage = "请确保产品名称的长度在3-50个字符之间!")]
10         [RegularExpression(@"^[a-zA-Z0-9'-'\s]*$", ErrorMessage = "请确保产品名称只能为字母或数字!")]
11         public string Name { get; set; }
12         [Required(ErrorMessage = "产品描述不能为空!")]
13         [StringLength(200, MinimumLength = 10, ErrorMessage = "请确保输入的产品描述信息的长度在10-200字符之间!")]
14         [RegularExpression(@"^[,;a-zA-Z0-9'-'\s]*$", ErrorMessage = "请确保产品描述信息只能是字母或数字!")]
15         [DataType(DataType.MultilineText)]
16         public string Description { get; set; }
17         [Required(ErrorMessage = "价格不能为空!")]
18         [Range(0.10, 10000, ErrorMessage = "请输入0.10-10000.00之间的价格!")]
19         [DataType(DataType.Currency)]
20         [DisplayFormat(DataFormatString = "{0:c}")]
21         public decimal Price { get; set; }
22         public int? CategoryID { get; set; }
23         public virtual Category Category { get; set; }
24     }
25 }

  这儿有多单我们在此以前从没显现了之初特点。代码[DataType(DataType.MultilineText]用来告诉UI当在编辑视图或创视图中,使用多写本来显示描述音信输入框。

  [DataType(DataType.Currency)]用于为UI指示应该如何限输入的格式。DataType的片段列目前尚非受多数浏览器所实现。

  [DisplayFormat(DataFormatString =
“{0:c})”]指令普赖斯(Price)属性应该让展现为货币格式,比如£1,234.56(倚重让地面服务器的钱设置)。一般景色下,这一个性都晤面做事,并且以标价展现为钱。为了完整性,大家拿它们都富含其中。

  现在,重新生成解决方案,然后在程序包管理器控制莱比锡相继运行下列命令,以向数据库添加范围与null设置。

1 add-migration ProductValidation
2 update-database

  不调试启动站点,然后点击产品链接。图4-13显了成品的列表,并且产品的价钱现在既深受格式化为货币模式,这还归功给我们以的多少评释功效。

Bootstrap 13

图4-13:产品目录(Index)页中的标价字段现在被格式化为货币模式

  注意:以代码[DisplayFormat(DataFormatString = “{0:c}”,
ApplyFormatInEditMode =
true)]得于大家于编写视图上校价格格式化为货币形式,可是,我们不引进这样做,因为,当大家在编辑是输入的价格格式为£9,999.99,然则当大家试试着提交表单时,价格以无会面透过认证,因为£不是一个数字。

  点击Details链接,我们以会面盼价格现在啊被格式化为货币形式。为了看对Product类所开的改带来的合震慑,我们需要创设和编辑一个出品。图4-14展现的是当创设一个新产品消息平时,所输入的数据总体请勿入规则的功用。

Bootstrap 14

图4-14:动用无效数据成立一个成品消息时所出示的自定义错误音讯

  译者注:原书第78页剩余部分所描述的问题好像不正确,有大神能看精通原作者意图的伏乞指出,在这么些卓殊感谢!由此译者对余下部分举行了修改,感兴趣之读者可参照原书。

  大家将表达应用及默认视图中是一致百般改进,然则,依然是有的略题目,比如,价格好输入多员小数,而我辈期待尽四只可以输入两位小数,为了化解这题目,大家应用下列模式。

  大家向Product类中补充加一个刚刚则发表注解,更新后的Price属性如下所示:

1 [Required(ErrorMessage = "价格不能为空!")]
2 [Range(0.10, 10000, ErrorMessage = "请输入0.10-10000.00之间的价格!")]
3 [DataType(DataType.Currency)]
4 [DisplayFormat(DataFormatString = "{0:c}")]
5 [RegularExpression("[0-9]+(\\.[0-9][0-9]?)?", ErrorMessage = "价格必须是不超过两位小数的数字!")]
6 public decimal Price { get; set; }

  那多少个正则表明式允许一个数值前边与一个或少于独小数,它同意的数值格式为1、1.1要么1.10,但未可以是1.从此没有其余数字之格式。图4-15展现了这么些认证效能。

Bootstrap 15

图4-15:对Product的Price(Price)属性应用两各项小数验证的效力

4.3.3 验证是肿么办事之

  当大家先是次创项目的时候,基架为我们机关装了少数只NuGet包:Microsoft.jQuery.Unobtrusive和jQuery.Validation.ASP.NET
MVC,它们运jQuery执行客户端验证,当用户从一个输入框跳到外一个输入框时,客户端验证即吃触发,也就是说用户不需交表单即可接验证的错误消息。

  当客户端验证通过后,提交表单也会晤接触服务端的认证。一些JavaScript代码可以绕了客户端验证,由此,服务端验证是最终一道防线。

  为了看服务端验证的效益,大家移除\Views\Bootstrap,Products\Create.cshtml文件中之下列代码(该代码位于该文件的终极):

1 @section Scripts {
2     @Scripts.Render("~/bundles/jqueryval")
3 }

  移除的即段代码首要用户客户端验证,以便大家现在就举办服务端验证。现在开行站点,并尝试创造一个空名称、空白描述和价格为1.234的成品,然后点击Create按钮。该页面将谋面展示和事先同一的认证信息,然而,本次是服务端执行的评释,如图4-16所出示。

Bootstrap 16

图4-16:交付表单之后的服务端验证

  将先移除的下列代码再另行长到\Views\Products\Create.cshtml文件中:

1 @section Scripts {
2     @Scripts.Render("~/bundles/jqueryval")
3 }

  编辑和成立视图文件不仅涵盖着对每个输入文本框生成验证音信的代码,还噙一个弯整个表单摘要的代码,该摘要首要用于转移有关模型的错误消息,和输入信息无关。\Views\Products\Create.cshtml文件被的代码大家以脚列有,高亮展现的代码行是跟讲明有关的代码。

 1 @model BabyStore.Models.Product
 2 
 3 @{
 4     ViewBag.Title = "Create";
 5 }
 6 
 7 <h2>Create</h2>
 8 
 9 
10 @using (Html.BeginForm()) 
11 {
12     @Html.AntiForgeryToken()
13     
14     <div class="form-horizontal">
15         <h4>Product</h4>
16         <hr />
17         @Html.ValidationSummary(true, "", new { @class = "text-danger" })
18         <div class="form-group">
19             @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
20             <div class="col-md-10">
21                 @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
22                 @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
23             </div>
24         </div>
25 
26         <div class="form-group">
27             @Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
28             <div class="col-md-10">
29                 @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
30                 @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
31             </div>
32         </div>
33 
34         <div class="form-group">
35             @Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
36             <div class="col-md-10">
37                 @Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
38                 @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
39             </div>
40         </div>
41 
42         <div class="form-group">
43             @Html.LabelFor(model => model.CategoryID, "CategoryID", htmlAttributes: new { @class = "control-label col-md-2" })
44             <div class="col-md-10">
45                 @Html.DropDownList("CategoryID", null, htmlAttributes: new { @class = "form-control" })
46                 @Html.ValidationMessageFor(model => model.CategoryID, "", new { @class = "text-danger" })
47             </div>
48         </div>
49 
50         <div class="form-group">
51             <div class="col-md-offset-2 col-md-10">
52                 <input type="submit" value="Create" class="btn btn-default" />
53             </div>
54         </div>
55     </div>
56 }
57 
58 <div>
59     @Html.ActionLink("Back to List", "Index")
60 </div>
61 
62 @section Scripts {
63     @Scripts.Render("~/bundles/jqueryval")
64 }

  代码@Html.ValidationSummary(true, “”, new { @class = “text-danger”
})负责展现对范的总体验证的摘要新闻,而其它几行代码,比如@Html.ValidationMessageFor(model
=> model.Name, “”, new { @class =
“text-danger”})用于体现每一个单独属性的证实信息(在斯事例中,是Name属性)。Bootstrap的CSS类text-danger用于将新闻展现为革命。

4.4 小节

  于及时同样节中,大家读了哪些修改Delete方法,以便可以去一个起外键约束之实体音讯,然后大家学了怎么启用Code
First
Migrations功效,以便大家得以经代码的修改来更新数据库架构。我们尚读了什么拔取迁移(Migration)对数据库添加种子数据以及哪些创立一个初数据库。最终,我们学了争向一个近乎添加格式和验证规则,然后采纳迁移(Migration)对数据库进行翻新,还探究了表明是哪行事的关于题材。

相关文章