6. 备用键
Alternate Keys是EF CORE引入的新功能,EF 6.X版本中并没有此功能。备用键可以用作实体中除主键和索引外的唯一标识符,还可以用作外键目标。在Fluent Api中,有两种方法可以指定备用键,一种是当开发者将实体中的属性作为另一个实体的外键目标,另一种是手动指定。EF CORE的默认约束是前者。
备用键和主键的作用十分相似,同样也存在复合备用键的功能,请大家注意区分。在要求单表列的一致性的场景中,使用唯一索引比使用备用键更佳。
1. Fluent API
public class MyContext : DbContext{ public MyContext(DbContextOptions<MyContext> options) : base(options) {
} public DbSet<Car> Cars { get; set; } public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 第一种方法
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogUrl)
.HasPrincipalKey(b => b.Url);
// 第二种方法
modelBuilder.Entity<Car>()
.HasAlternateKey(c => c.LicensePlate)
.HasName("AlternateKey_LicensePlate");
}
}public class Car{ public int CarId { get; set; } public string LicensePlate { get; set; } public string Make { get; set; } public string Model { get; set; }
}public class Blog{ public int BlogId { get; set; } public string Url { get; set; } 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 string BlogUrl { get; set; } public Blog Blog { get; set; }
}
上述代码中的第一种方法指定Post实体中的BlogUrl属性作为Blog对应Post的外键,指定Blog实体中的Url属性作为备用键(HasPrincipalKey方法将在下文的唯一标识节中讲解),此时Url将被配置为唯一列,扮演BlogId的作用。
7. 计算列
计算列指的是列的数据由数据库计算生成,在EF CORE层面,我们只需要定义计算规则即可。目前EF CORE 1.1 版本中,暂不支持使用Data Annotations方式定义。
1 Fluent API
class MyContext : DbContext{ public DbSet<Person> People { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Person>()
.Property(p => p.DisplayName)
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
}
}public class Person{ public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string DisplayName { get; set; }
}
以上代码指明DisplayName由LastName和FirstName结合计算而成,这项工作由数据库代劳,查看P的视图设计器,我们也可以发现数据库在生成表时便指定了详细规则。
CREATE TABLE [dbo].[People] (
[PersonId] INT IDENTITY (1, 1) NOT NULL,
[DisplayName] AS (([LastName]+', ')+[FirstName]),
[FirstName] NVARCHAR (MAX) NULL,
[LastName] NVARCHAR (MAX) NULL, CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED ([PersonId] ASC)
);
8. 生成值
前文中已经介绍过,假如属性被命名为Id/[TypeName]Id
的形式,EF CORE会将该属性设置为主键。进一步说,如果属性是整数或是Guid类型,那么该属性将会被EF CORE设置为自动生成。这是EF CORE的语法糖之一。
那由用户手动设置呢?EF CORE在Data Annotations和Fluent Api形式上为开发者分别提供了三种方法。
1 Data Annotations
public class Blog{
[DatabaseGenerated(DatabaseGeneratedOption.None)] public int BlogId { get; set; } public string Url { get; set; }
}
public class Blog{ public int BlogId { get; set; } public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] public DateTime Inserted { get; set; }
}
public class Blog{ public int BlogId { get; set; } public string Url { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)] public DateTime LastUpdated { get; set; }
}
2 Fluent API
modelBuilder.Entity<Blog>()
.Property(b => b.BlogId)
.ValueGeneratedNever();
modelBuilder.Entity<Blog>()
.Property(b => b.Inserted)
.ValueGeneratedOnAdd();
modelBuilder.Entity<Blog>()
.Property(b => b.LastUpdated)
.ValueGeneratedOnAddOrUpdate();
值得注意的是,上述对DateTime类型的自动添加操作都是不可行的,这是因为EF CORE只支持部分类型的自动操作,详见Default Values。对于DateTime类型,我们可以用以下代码实现自动插入
modelBuilder.Entity<Blog>().Property(b => b.Created).HasDefaultValueSql("getdate()");
,
这也是第7点默认值的一种用法。
9. 默认值
默认值与计算列定义十分相似,只是计算列无法由用户手动输入。而默认值更多指的是当用户不手动输入时,使用默认值进行数据库相应列的填充。以下代码表示假如操作中不指定Rating的值,那么数据库将默认填充3。
class MyContext : DbContext{ public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
}
}public class Blog{ public int BlogId { get; set; } public string Url { get; set; } public int Rating { get; set; }
}
10. 索引
EF CORE中的索引概念和关系型数据库中的索引概念没有什么不同,比如在Sql Server,将Blog映射到数据库时,将为BlogId建立主键默认持有的聚集索引,将Post映射到数据库中时,将为Post的BlogId建议外键默认的非聚集索引。
GOCREATE NONCLUSTERED INDEX [IX_Posts_BlogId] ON [dbo].[Posts]([BlogId] ASC);
至于为一个或多个属性手动建立索引,可以使用形如以下代码。
1. Fluent API
class MyContext : DbContext{ public DbSet<Blog> Blogs { get; set; } public DbSet<Person> People { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Blog>()
.HasIndex(b => b.Url)
.HasName("IX_Url");
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.FirstName, p.LastName });
}
}public class Blog{ public int BlogId { get; set; } public string Url { get; set; }
}public class Person{ public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; }
}
假如你需要配置一个唯一索引,请使用IsUnique方法。形如以下代码:
modelBuilder.Entity<Blog>()
.HasIndex(b => b.Url)
.HasName("IX_Url")
.IsUnique();