通过 Fluent API 在 IEntityTypeConfiguration 实现类里面配置实体:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.HasKey(t => t.BlogId);
builder.Property(t => t.Url).IsRequired().HasMaxLength(500);
}
}
并在 Context 的 OnModelCreating 方法里面应用:
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new BlogConfiguration());
}
}
Fluent API 比数据注解有更高的优先级。
实体关系
一对多关系
Blog 和 Post 是一对多关系,在 PostConfiguration 里面添加如下配置:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { 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 BlogId { get; set; }
public Blog Blog { get; set; }
}
public class PostConfiguration : IEntityTypeConfiguration<Post>
{
public void Configure(EntityTypeBuilder<Post> builder)
{
builder.HasOne<Blog>(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
}
}
一对一关系
创建一个实体类 PostExtension 做为 Post 的扩展表,它们之间是一对一关系。
如果两个实体相互包括了对方的引用导航属性(本例中是 PostExtension Extension 和 Post Post)和外键属性 (本例中是 PostExtension 中的 PostId),那 EF Core 会默认配置一对一关系的,当然也可以手动写语句(如注释的部分)。
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public PostExtension Extension { get; set; }
}
public class PostExtension
{
public int PostId { get; set; }
public string ExtensionField1 { get; set; }
public Post Post { get; set; }
}
public class PostExtensionConfiguration : IEntityTypeConfiguration<PostExtension>
{
public PostExtensionConfiguration()
{
}
public void Configure(EntityTypeBuilder<PostExtension> builder)
{
builder.HasKey(t => t.PostId);
//builder.HasOne(e => e.Post)
// .WithOne(p => p.Extension)
// .HasForeignKey<PostExtension>(e => e.PostId)
// .OnDelete(DeleteBehavior.Cascade);
}
}
多对多关系
创建一个实体类 Tag, 和 Blog 是多对多关系。一个 Blog 可以有多个不同 Tag,同时一个 Tag 可以用多个 Blog。
EF Core 中创建多对多关系必须要声明一个映射的关系实体,所以我们创建 BlogTag 实体,并在 BlogTagConfiguration 配置了多对多关系。
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public IList<BlogTag> BlogTags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
public IList<BlogTag> BlogTags { get; set; }
}
public class BlogTag
{
public int BlogId { get; set; }
public Blog Blog { get; set; }
public int TagId { get; set; }
public Tag Tag { get; set; }
}
public class BlogTagConfiguration : IEntityTypeConfiguration<BlogTag>
{
public void Configure(EntityTypeBuilder<BlogTag> builder)
{
builder.HasKey(bt => new { bt.BlogId, bt.TagId });
builder.HasOne<Blog>(bt => bt.Blog)
.WithMany(b => b.BlogTags)
.HasForeignKey(bt => bt.BlogId);
builder.HasOne<Tag>(bt => bt.Tag)
.WithMany(t => t.BlogTags)
.HasForeignKey(bt => bt.TagId);
}
}
种子数据
填充种子数据可以让我们在首次使用应用之前向数据库中插入一些初始化数据。有两种方法:
通过实体类配置实现
在配置实体的时候可以通过HasData方法预置数据,在执行Update-Database命令时候会写入数据库。
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
//Data Seeding
builder.HasData(new Blog { BlogId = 1, Url = "http://sample.com/1", Rating = 0 });
}
}
统一配置
创建一个统一配置 SeedData 类, 然后在 Program.cs 中的 Main 中调用它。
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new BloggingContext(
serviceProvider.GetRequiredService<DbContextOptions<BloggingContext>>()))
{
if (context.Blogs.Any())
return; // DB has been seeded
var blogs = new List<Blog>
{
new Blog
{
Url = "http://sample.com/2",
Rating = 0
},
new Blog
{
Url = "http://sample.com/3",
Rating = 0
},
new Blog
{
Url = "http://sample.com/4",
Rating = 0
}
};
context.Blogs.AddRange(blogs);
context.SaveChanges();
}
}
}
public class Program
{
public static void Main(string[] args)
{
//CreateWebHostBuilder(args).Build().Run();
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
并发管理