只有学习,内心才能踏实。
今天来总结一下,EF Core 中Code Frist 的数据注解及 Fluent API。其实这个次总结是为了巩固一下以前的知识,如果比较懂EF ,这部知识可以快速过。但是!但是!EF Core 和 EF 还是有很大区别的,比如说:默认值和索引等用数据注解的方式在.Net Core 无效,只能用Fluent API. 本文记录两种方式来创建模型,分别是数据注解方式和Fluent API
在这里有很多东西可以扩展,但是由于本人能力有限,无法求证。文末会总结遗留的问题,有大神看到可以帮忙回答一下。EF Core 的版本为2.1
1.表映射 [Table(string name, Properties:[Schema = string])
[Table("DataTest", Schema = "admin")]
//注释:[Table(string name, Properties:[Schema = string])
public class DataAnnotationsAttribute
{
[Key]
[Column(Order = 1)]
public int Id { get; set; }
}
2.列映射 [Column (string name, Properties:[Order = int],[TypeName = string])
[Table("DataTest", Schema = "admin")]
//注释:[Table(string name, Properties:[Schema = string])
public class DataAnnotationsAttribute
{
[Key]
[Column(Order = 1)]
public int Id { get; set; }
[Column(Order = 3, TypeName = "varchar(50)")]
public string Name { get; set; }
[Column("FullName", Order = 2, TypeName = "varchar(60)")]
// [Column (string name, Properties:[Order = int],[TypeName = string])
public string FullName { get; set; }
// [ForeignKey] 参考 UserRole
[DefaultValue(3)]
public int DefaultValue { get; set; }
}
Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(eb =>
{
eb.Property(b => b.Name).HasColumnType("varchar(50)");
eb.Property(b => b.FullName).HasColumnType("varchar(60)");
});
}
数据类型:这点,有时间的话,可以从网上查看一下啊,EF 的数据类型 对应数据库的数据类型
3. 主键 [key]
数据注解方式:
[Key]
[Column(Order = 1)]
public int Id { get; set; }
Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity() .HasKey(b => b.BlogId).HasName("PrimaryKey_BlogId");
}
数据库图:略
4. 复合主键
Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().HasKey(c => new { c.LicensePlate, c.State });
}
5. 计算列(列计算或拼接):数据注解中无法实现,只能在Fluent API中实现
class MyContext : DbContext
{
public DbSet People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.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; }
}
数据库图:略
6.序列:数据注解中无法实现,只能在Fluent API中实现
你可以配置它如其实值从1000 开始:StartsAt(1000);每次增5:IncrementsBy(5)
也可以从模型中取值,比如下面 NEXT VALUE FOR shared.OrderNumbers
class MyContext : DbContext
{
public DbSet Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasSequence("OrderNumbers", schema: "shared")
.StartsAt(1000)
.IncrementsBy(5);
modelBuilder.Entity()
.Property(o => o.OrderNo)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
}
public class Order
{
public int OrderId { get; set; }
public int OrderNo { get; set; }
public string Url { get; set; }
}
7.默认值:数据注解中无法实现(跟EF 不一样,即使提供,但没有效果),只能在Fluent API中实现
字段上加[DefaultValue(3)] 是没有效果的
class MyContext : DbContext
{
public DbSet Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(b => b.Rating)
.HasDefaultValue(3);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
}
8.索引:数据注解中无法实现(跟EF 不一样,即使提供,但没有效果),只能在Fluent API中实现
是否唯一:IsUnique()
IsClustered 在.net core 没有发现本人不敢确认有没有
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 唯一索引
modelBuilder.Entity().HasIndex(b => b.Url).IsUnique();
// 非唯一
modelBuilder.Entity().HasIndex(b => new { b.RegistrationNumber1, b.RegistrationNumber2 });
}
9:外键约束
一对多:
// 实体
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int CurrentGradeId { get; set; }
public Grade Grade { get; set; }
}
public class Grade
{
public int GradeId { get; set; }
public string GradeName { get; set; }
public string Section { get; set; }
public ICollection Students { get; set; }
}
// Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasOne(s => s.Grade)
.WithMany(g => g.Students)
.HasForeignKey(s => s.CurrentGradeId);
}
public DbSet Grades { get; set; }
public DbSet Students { get; set; }
一对一:
// 实体
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public StudentAddress Address { get; set; }
}
public class StudentAddress
{
public int StudentAddressId { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public int AddressOfStudentId { get; set; }
public Student Student { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasOne(s => s.Address)
.WithOne(ad => ad.Student)
.HasForeignKey(ad => ad.AddressOfStudentId);
}
public DbSet Students { get; set; }
public DbSet StudentAddresses { get; set; }
多对多:
// 实体
public class StudentCourse
{
public int StudentId { get; set; }
public Student Student { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public IList StudentCourses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public string Description { get; set; }
public IList StudentCourses { get; set; }
}
// Fluent API
modelBuilder.Entity().HasKey(sc => new { sc.SId, sc.CId });
modelBuilder.Entity()
.HasOne(sc => sc.Student)
.WithMany(s => s.StudentCourses)
.HasForeignKey(sc => sc.SId);
modelBuilder.Entity()
.HasOne(sc => sc.Course)
.WithMany(s => s.StudentCourses)
.HasForeignKey(sc => sc.CId);
10:排除实体和属性--NotMapped
// 实体
////排除entity
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; }
}
////排除属性
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
[NotMapped]
public DateTime LoadedFromDatabase { get; set; }
}
//Fluent API
//// 排除entity
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore();
}
//// 排除属性
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Ignore(b => b.LoadedFromDatabase);
}
11:最大长度--MaxLength (Fluent API 中没有MinLength)
//实体
public class Blog
{
public int BlogId { get; set; }
[MaxLength(500)]
public string Url { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(b => b.Url)
.HasMaxLength(500);
}
12:防并发:Timestamp 与ConcurrencyCheck
//实体
public class Person
{
public int PersonId { get; set; }
[ConcurrencyCheck]
public string LastName { get; set; }
//10 .Timestamp 时间戳必须是byte[]类型的,防止并发,EF 的并发都是乐观的。例如同时改一条数据,别人在你之前提交
[Timestamp]
public byte[] Timestamp { get; set; }
}
//Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.Property(p => p.Timestamp)
.IsRowVersion();
modelBuilder.Entity().Property(b => b.Timestamp).IsRowVersion();
}
13: 值转换 (.net core 2.1 新增)
和14 一起举例在进行数据库迁移时,EF会往数据库中插入一些数据
14:Data Seeding (.net core 2.1 新增)这个是用来初始化数据用。
// 值类型转换
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public EquineBeast Mount { get; set; }
}
public enum EquineBeast
{
Donkey,
Mule,
Horse,
Unicorn
}
// fluent api
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity()
.Property(e => e.Mount)
.HasConversion(
v => v.ToString(),
v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
// DATA SEEDING
modelBuilder.Entity().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
}
数据迁移后的结果是:
除了基本的枚举转字符串以外,EF Core还提供如下的转换类:
BoolToZeroOneConverter | 将布尔值转换为0或1 |
BoolToStringConverter | 将布尔值转换为字符串(Y或N) |
BoolToTwoValuesConverter | 将布尔值转换为指定的两个值(没搞明白干嘛用的) |
BytesToStringConverter | 将字节数组转换为Base64编码的字符串 |
CastingConverter | 从一种类型转换到另一种类型(可以被C#互相转换的类型) |
CharToStringConverter | char转为string |
DateTimeOffsetToBinaryConverter | DateTimeOffset转为二进制的64位的值 |
DateTimeOffsetToBytesConverter | DateTimeOffset转为字节数组 |
DateTimeOffsetToStringConverter | DateTimeOffset转为字符串 |
DateTimeToBinaryConverter | DateTime转为带有DateTimeKind的64位的值 |
DateTimeToStringConverter | DateTime转为字符串 |
DateTimeToTicksConverter | DateTime转为ticks |
EnumToNumberConverter | 枚举转数字 |
EnumToStringConverter | 枚举转字符串 |
GuidToBytesConverter | Guid转字节数组 |
GuidToStringConverter | Guid转字符串 |
NumberToBytesConverter | 数字转字节数组 |
NumberToStringConverter | 数字转字符串 |
StringToBytesConverter | 字符串转字节数组 |
TimeSpanToStringConverter | TimeSpan转字符串 |
TimeSpanToTicksConverter | TimeSpan转ticks |
上面的这些对象的使用方式如下:
var converter = new EnumToStringConverter();
builder.Property(p => p.Gender).HasConversion(converter);
除了这种方式外,EF Core也支持直接指定类型,如:
builder.Property(p => p.Gender).HasConversion(string);
需要注意的是,不能将null进行转换,一个属性只能对应一个列做转换。
15:查询类型-Query Types (.net core 2.1 新增)
ToView等
这就不多写了,我感觉比较重要,怕误导大家,所以粘出官方文档连接
https://docs.microsoft.com/zh-cn/ef/core/modeling/query-types
16:实体构造函数 (.net core 2.1 新增)
https://docs.microsoft.com/zh-cn/ef/core/modeling/constructors
17:固有实体类型(.net core 2.0 新增)
https://docs.microsoft.com/zh-cn/ef/core/modeling/owned-entities
总结
1:文章写到最后,还是没有全部坚持下来,比如说第15,16,17 没有写例子。其实我感觉他们都还挺重要。有时间了,我还会把这些内容补回来。
2:.net ef core 跟 .net ef 还是有很多差别的地方,有很多无法使用数据注解的方式解决,只能使用 fluent api。但是例如StringLength 注解,加上就是非空的。但是在fluent api 中没有发现这个。
3:欢迎大家指点错误和指导。
只有学习,内心才能踏实,我是CHIASING