一.模型配置概述
EF使用一组约定基于实体类的定义来构建模型。 可指定其他配置以补充或替代约定的内容。本系列介绍的配置可应用于面向任何数据存储的模型,以及面向任意关系数据库时可应用的配置。
数据库提供程序还可支持特定于具体数据存储的配置,如Microsoft.EntityFrameworkCore.SqlServer,Pomelo.EntityFrameworkCore.MySql 等,对于特定配置的文档参考数据库提供程序。
1.1 使用 fluent API 配置模型
可在派生上下文中重写 OnModelCreating
方法,并使用 ModelBuilder API
来配置模型。 此配置方法最为有效,并可在不修改实体类的情况下指定配置。 Fluent API 配置具有最高优先级,并将替代约定和数据注释。下面示例指定Blog类型的Url在保存时必填。如下所示:
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
}
1.2 使用数据注释来配置模型
也可将特性(称为数据注释)应用于类和属性。 数据注释会替代约定,但会被 Fluent API 配置替代。下面示例指定Blog类型的Url在保存时必填。如下所示:
public class Blog
{
public int BlogId { get; set; }
[Required(ErrorMessage = "请输入URL")]
public string Url { get; set; } public ICollection<Post> Posts { get; set; }
}
[HttpPost]
public async Task<IActionResult> Create([Bind("Url")] Blog blog)
{
if (ModelState.IsValid)
{
BloggingContext.Add<Blog>(blog);
await BloggingContext.SaveChangesAsync();
}
return View();
}
在MVC中, 新增一条Blog数据,Url 字段为空(包含空格),点击提交时,在后台Create中验证,设置断点查看ModelState.IsValid为false。
其中取Url字段的ErrorMessage信息是:ModelState["URL"].Errors[0].ErrorMessage
二.类型的包含和排除约定
将类型(实体类型)包含到模型中意味着,EF会有该类型的元数据,并且会尝试从数据库读取实例(读取对应的数据表),以及将实例(实体对象数据)写入到数据库中。按照约定,在上下文的 DbSet
属性中公开的类型(实体类型)会包含在模型中。 此外,在 OnModelCreating
方法中提及的类型(实体类型)也将包含在其中。 最后,通过以递归方式浏览已发现类型的导航属性而找到的任何类型也会包含在模型中。下面举例一 一说明:
2.1 类型包含约定
class MyContext : DbContext
{
//公开Blog类型,将与数据库表产生映射关系
public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//公开AuditEntry类型,将与数据库表产生映射关系
modelBuilder.Entity<AuditEntry>();
}
} public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } //公开Post类型,通过导航属性。将与数据库表产生映射关系
public List<Post> Posts { get; set; }
} public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; } public Blog Blog { get; set; }
} public class AuditEntry
{
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
}
上面示例中:
(1)Blog
,因为它是在上下文的 DbSet
属性中公开的。(2)Post
,因为它是通过 Blog.Posts
导航属性发现的。(3) AuditEntry
,因为它在 OnModelCreating
中提及。
2.2 类型排除约定
(1)可以使用数据注释来从模型中排除类型。
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public BlogMetadata Metadata { get; set; }
} [NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
(2)也可以通过Fluent API 把模型中类型排除。
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
}
} public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public BlogMetadata Metadata { get; set; }
} public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
三.属性的包含和排除约定
模型中包含属性意味着 EF 拥有该属性的元数据,并将尝试从数据库读取值或者向数据库写入值。按照约定,模型所含的那些公共属性都拥有一个 getter 和一个 setter。
3.1 数据注释
可以使用数据注释方式来从模型中排除某个属性
public class Blog
{
//公开的属性
public int BlogId { get; set; }
public string Url { get; set; } //排除的属性
[NotMapped]
public DateTime LoadedFromDatabase { get; set; }
}
3.2 Fluent API
也可以用Fluent API 从模型中排除某个属性。
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Ignore(b => b.LoadedFromDatabase);
}
} public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } public DateTime LoadedFromDatabase { get; set; }
}
参考文献:
官方资料:创建并配置模型