第4章 模型
实体框架
使用
1.引用:
使用NuGet安装Entity Framework ,引入的是EntityFramework EntityFramework.SqlServer包,之后就可以使用System.Data.Entity
2.配置连接:
配置数据库连接字符串,name与DbContext类的类名一样
<connectionStrings>
<add name="DBContext" connectionString="Data Source=;Initial Catalog=;User Id=;Password=;"
providerName="System.Data.SqlClient" />
</connectionStrings>
3.创建DbContext子类:
public class DBContext:DbContext
{
public DBContext() : base("name=TestDB") //base指定某个名字,默认与类同名
{
}
public DbSet<Models.User> Users { get; set; } //持久保存的对象
}
4.播种数据库和设置数据库初始化器:
public class DBInitializer : System.Data.Entity.DropCreateDatabaseAlways<DBContext>
{
protected override void Seed(DBContext context)
{
context.Users.Add(new User { UserName = "test001", PassWord = "333", Name = "测试员" });
base.Seed(context);
}
}
在Global.asax中第一行添加 Database.SetInitializer(new DBInitializer()); 作为每次登陆时都创建数据库
CodeFirst约定
1.类型发现:
当使用 Code First 开发时,通常是从编写用来定义概念(域)模型的 .NET Framework 类开始。还需要让 DbContext 知道模型中要包含哪些类。
需要定义一个派生自DbContext的上下文类,并公开需要成为模型一部分的类型的 DbSet 属性。
Code First 将包含这些类型,还将包含任何引用类型,即使这些引用类型是在不同的程序集中定义的也是如此。
如果类型存在于继承层次结构中,则为基类定义 DbSet 属性就足够了.如果派生类型位于与基类相同的程序集中,则自动包含这些派生类型。
如果要从模型排除类型,请使用 NotMapped 特性或 DbModelBuilder.Ignore Fluent API。modelBuilder.Ignore<Department>();
2.主键约定
如果类的属性名为“ID”(不区分大小写)或类名的后面跟有“ID”,则 Code First 会推断该属性是主键。
如果主键属性的类型为数值或 GUID,则将其配置为标识列。
3.关系约定
实体框架中的导航属性提供了一种在两个实体类型之间导航关系的方法。针对对象参与到其中的每个关系,各对象均可以具有导航属性。
使用导航属性可以在两个方向上导航和管理关系,返回引用对象(如果多重性为一或者零或一)或集合(如果多重性为多)。
Code First 根据针对类型定义的导航属性来推断关系。
除导航属性外,建议还要包括表示依赖对象的类型的外键属性。任何数据类型与主体主键属性相同、遵循以下一种格式的属性都表示关系的外键:
“<导航属性名称><主体主键属性>”、“<主体类名><主键属性名称>”或“<主体主键属性名称>”。
如果找到多个匹配项,则优先级符合上面列出的顺序。外键检测不区分大小写。
在检测外键属性时,Code First 基于外键的可空性推断关系的多重性。
如果属性可以为 Null,则将关系注册为可选关系,否则将关系注册为必需关系。
如果依赖实体上的外键不能为 Null,则 Code First 对关系设置级联删除。
如果依赖实体上的外键可以为 Null,则 Code First 不对关系设置级联删除,并且在删除主体时,会将该外键设置为 Null。
通过使用 Fluent API,可以覆盖由约定检测的多重性和级联删除行为。
如果相同类型间有多个关系,则需要使用数据注释或 Fluent API 手动配置关系。
4.复杂类型约定
当 Code First 发现无法推断主键以及未通过数据注释或 Fluent API 注册主键的类时,类型会自动注册为复杂类型。
复杂类型检测还要求类型不具有引用实体类型的属性,并且未被其他类型的集合属性引用。
5.移除约定
可以移除在 System.Data.Entity.ModelConfiguration.Conventions 命名空间中定义的任何约定。
下面的示例移除 PluralizingTableNameConvention。
public class SchoolEntities : DbContext
{
. . .
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention, the generated tables
// will have pluralized names.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
加载相关对象
预加载策略,尽可能地使用查询语句加载所有数据.
延迟加载策略,EF在LINQ查询中只加载主要对象,并根据需要加载相关数据.
模型
基架
代码生成的过程叫做基架.例如通过选择创建视图代码的模板,自动生成了视图代码.
基架也可以创建CRUD的样本代码,通过检测模型类的定义,生成控制器以及与之关联的视图.
基架知道如何命名控制器,命名视图以及每个组件需要执行什么代码,也知道如何放置这些项.
path
happy path当模型处于有效状态并可以将对象保存到数据库是执行的代码路径.
ModelState.IsValid
sad path当模型无效时操作采用的路径.通常会重新创建视图以便用户纠正错误.
不过大多数错误在可以在客户端验证.(第6章)
模型绑定
当操作带有一个参数时,MVC运行环境就会使用一个模型绑定器来构建这个参数.
根据参数对象属性,在请求中查找同名的参数.
如果操作中没有参数,也可以使用控制器中的UpdateModel和TryUpdateModel显示的调用模型.
[HttpPost]
public ActionResult Edit()
{
var album = new Album();
try{
UpdateModel(album);
...}
catch{
some viewbag ex.
}
}
[HttpPost]
public ActionResult Edit()
{
var album = new Album();
if(TryUpdateModel(album){ ... }
else{ ... }
}
模型绑定后就有了模型状态,模型绑定器移进模型的每一个值都在模型状态中有一个记录,可以随时查看是否绑定成功:
if(ModelState.IsValid)
需要避免重复提交(第7章)
模型操作
Add:
if(ModelState.IsValid)
{
db.Users.Add(album);
db.SaveChanges();
}
Edit:
if(ModelState.IsValid)
{
db.Entry(album).State= EntityState.Modified;
db.SaveChanges();
}
Remove:
if(ModelState.IsValid)
{
db.Roles.Remove(album);
db.SaveChanges();
}