一、初步使用
- 依赖注入设置
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
}
- 创建DbContext
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
- 关于构造函数 —— EF不关心private/public 它都可以调用
- EF会调用默认无参构造函数,然后把对应的属性值显式设置为数据库中的值
- 但如果EF 查找到参数名称(首字母大小写不敏感)和类型都匹配,将调用该构造函数,对于其他的一些属性,它将通过正常显式设置
- 只读属性是不会被设置的 —— 不会按约定映射没有 setter 的属性。 (这样做通常是为了映射不应映射的属性,如计算属性。 )
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
二、Fluent Api使用
- 模型配置
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditEntry>();
}
}
- 忽略某个类型
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<BlogMetadata>();
}
- 忽略/排除属性
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Ignore(b => b.LoadedFromDatabase);
}
- 指定表名
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs");
}
- 配置列名
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.BlogId)
.HasColumnName("blog_id");
}
- 配置列的数据类型
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
eb =>
{
eb.Property(b => b.Url).HasColumnType("varchar(200)");
eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
});
}
- 配置最大长度
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasMaxLength(500);
}
- 显示配置必须的
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
- 排序规则
modelBuilder.Entity<Customer>().Property(c => c.Name)
.UseCollation("SQL_Latin1_General_CP1_CI_AS");
- 配置主键
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasKey(c => c.LicensePlate);
}
//配置主键约束名称
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(b => b.BlogId)
.HasName("PrimaryKey_BlogId");
}
三、Fluent Api 生成默认值
// 固定值
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Rating)
.HasDefaultValue(3);
}
// 当前日期
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
四、实体之间的关系
HasOne/HasMany 用以标识实体的导航属性
WithOne/WithMany 用以标识实体的反向导航属性
- 单个导航属性
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(b => b.Post)
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public Post Post { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
- 配置外键
internal class MyContext : DbContext
{
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.BlogForeignKey);
}
}
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 int BlogForeignKey { get; set; }
public Blog Blog { get; set; }
}
- 级联删除
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.OnDelete(DeleteBehavior.Cascade);
}
- 1对1关系
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(p => p.BlogImage )
.WithOne(b => b.Blog);
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public BlogImage BlogImage { get; set; }
}
public class BlogImage
{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- 多对多 - 该关系是通过一个额外的联接表来表示的
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
CREATE TABLE [Posts] (
[PostId] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId])
);
CREATE TABLE [Tags] (
[TagId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([TagId])
);
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([PostId]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tags_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([TagId]) ON DELETE CASCADE
);
五、索引
- 简单索引
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasIndex(b => b.Url);
}
- 复合索引
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.FirstName, p.LastName });
}
- 唯一索引
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasIndex(b => b.Url)
.IsUnique();
}
六、值类型转换
- 把某个字段存为字符串,借而取的时候转换为对象
modelBuilder.Entity<TaskRule.Domain.TaskRules.TaskRule>()
.Property(e => e.UrlConfig)
.HasConversion(v => JsonConvert.SerializeObject(v),
s => JsonConvert.DeserializeObject<UrlConfig>(s));
- 内置转换器
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//把一个int类型转为bool类型
modelBuilder
.Entity<User>()
.Property(e => e.IsActive)
.HasConversion<int>();
}
- 定义一个Converter
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var converter = new ValueConverter<EquineBeast, string>(
v => v.ToString(),
v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion(converter);
}
七、继承 映射生成Table
- 对于如下的继承关系类
public class Resort : Lodging //这里继承了Lodging类
{
public string Entertainment { get; set; } //娱乐
public string Activities { get; set; } //活动
}
- TPH(Table Per Hierarchy) —— 建立一个大表,所有列都在该表里面,有一个列标识是来着什么类的。
这是EF的默认的继承映射关系:一张表存放基类和子类的所有列,自动生成的discriminator列用来区分基类和子类的数据
如上,实际会生成:
image.png
这种就是把所有的字段都生成到了一张表里面
- TPT (Table Per Type) —— 每个之类都生成一个表,但是只包含之类自己的列(不含基类的列)
-
比如这个例子会生成两个表,通过LodgingId来匹配
image.png
- TPC(Table Per Concrete Type)—— 每个之类都建一个表。子类的表的列会包含它继承的父类的所有字段
- 当前例子下,会生成如下的表
image.png
官网结构介绍文档
八、性能诊断
- 简单日志记录
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Integrated Security=True")
.LogTo(Console.WriteLine, LogLevel.Information);
}
输出大概如下:
info: 06/12/2020 09:12:36.117 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] = N'foo'
- 基准测试 —— benchmarkdotnet
方法 | 平均值 | 错误 | 标准偏差 | 中值 | 比率 | RatioSD | 第0代 | 第1代 | 第2代 | 已分配 |
---|---|---|---|---|---|---|---|---|---|---|
LoadEntities | 2860.4 | 54.31 | 93.68 | 2844.5 | 4.55 | 0.33 | 210.9375 | 70.3125 | - | 1309.56 KB |
LoadEntitiesNoTracking | 1353.0 | 21.26 | 18.85 | 1355.6 | 2.10 | 0.14 | 87.8906 | 3.9063 | - | 540.09 KB |
ProjectOnlyRanking | 910.9 | 20.91 | 61.65 | 892.9 | 1.46 | 0.14 | 41.0156 | 0.9766 | - | 252.08 KB |
CalculateInDatabase | 627.1 | 14.58 | 42.54 | 626.4 | 1.00 | 0.00 | 4.8828 | - | - | 33.27 KB |
网友评论