最新版本号[免费下载]

EntityFramework Core 学习扫盲 (下)

作者:本站编辑 发布时间:2018-09-30 来源:佚名 点击数:

11. 主体和唯一标识

在这一节中,让我们来回顾一下HasPrincipalKey方法和唯一标识。

在EF CORE中,主体(Principal Entity)指的是包含主键/备用键的实体。所以在一般情况下,所有的实体都是主体。而主体键(Principal Key)指的是主体中的主键/备用键。大家都知道,主键/备用键都是不可为空且唯一的,这就引出了唯一标识列的写法。

唯一标识列一般有“主体键”,“唯一索引”两种写法,其中主体键中的主键没有什么讨论的价值。让我们来看看其他两种的写法。

1. 备用键

备用键在之前的小节中已经提过,使用以下代码配置的列将自动设置为唯一标识列。

modelBuilder.Entity<Car>()
                .HasAlternateKey(c => new {c.LicensePlate, c.Model})
                .HasName("AlternateKey_LicensePlate");
                
modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog)
                .WithMany(b => b.Posts)
                .HasForeignKey(p => p.BlogUrl)
                .HasPrincipalKey(b => b.Url);

注意这里的HasPrincipalKey方法,它通常跟在HasForeignKey和WithMany方法后,用以指定实体中的一个或多个属性作为备用键。再次重申一遍,备用键和主键有相似之处,它通常用来指定一个明确的外键目标——当开发者不想用单纯无意义的Id作为外键标识时。

虽然主体键也包括主键,但是主键在EF CORE中时强制定义的,所以HasPrincipalKey只会将属性配置为备用键。

2. 唯一索引

索引及其唯一性只由Fluent Api方式指定,由索引来指定唯一列是比备用键更好的选择。

modelBuilder.Entity<Blog>()
            .HasIndex(b => b.Url)
            .IsUnique();

12. 继承

继承通常被用来控制实体类接口如何映射到数据库表结构中。在EF CORE 当前版本中,TPC和TPT暂不被支持,TPH是默认且唯一的继承方式。

那么什么是TPH(table-per-hierarchy)呢?顾名思义,一种继承结构全部映射到一张表中,比如Person父类,Student子类和Teacher子类,由EF CORE映射到数据库中时,将会只存在Person类,而Student和Teacher将以列标识的形式出现。

目前只有Fluent Api方式支持TPH,具体实体类代码如下,其中RssBlog继承自Blog。

public class Blog{    public int BlogId { get; set; }    public string Url { get; set; }
}public class RssBlog : Blog{    public string RssUrl { get; set; }
}

在MyContext中,我们将原来的代码修改成如下形式:

class MyContext : DbContext{    public DbSet<Blog> Blogs { get; set; }    protected override void OnModelCreating(ModelBuilder modelBuilder)    {
        modelBuilder.Entity<Blog>()
            .HasDiscriminator<string>("blog_type")
            .HasValue<Blog>("blog_base")
            .HasValue<RssBlog>("blog_rss");
    }
}public class Blog{    public int BlogId { get; set; }    public string Url { get; set; }
}public class RssBlog : Blog{    public string RssUrl { get; set; }
}

观察OnModelCreating方法,HasDiscriminator提供修改标识列名的功能,HasValue提供新增或修改实体时,根据实体类型将不同的标识自动写入标识列中。如新增Blog时,blog_type列将写入blog_base字符串,新增RssBlog时,blog_type列将写入blog_rss字符串。

笔者不推荐用继承的方式设计数据库,只是这个功能相对新奇,就列出来说了。

13. 关系

关系型数据库模型的设计中,最重要的一点便是“关系”的设计了。常见的关系有1-1,1-n,n-n,除此以外,关系的两边还有可空不可空的控制。那么在EF CORE中,我们怎么实现这些关系呢?

以下内容用代码的方式给出了一对一,一对多和多对多的关系,两边关系设为不可空。其实可空不可空的控制十分简单,只要注意是否需要加上IsRequired的扩展Api即可。

不得不说,相比EF6.X的HasRequired和WithOptional等方法,EF CORE中的Api和关系配置清晰直观了不少。

唯一需要注意的是,关系设置请从子端(如User和Blog呈一对多对应时,从Blog开始)开始,否则配置不慎容易出现多个外键的情况。

public class MyContext : DbContext
    {        public MyContext(DbContextOptions<MyContext> options) : base(options)        {
        }        public DbSet<User> Users { get; set; }        public DbSet<UserAccount> UserAccounts { get; set; }        public DbSet<Blog> Blogs { get; set; }        public DbSet<Post> Posts { get; set; }        public DbSet<PostTag> PostTags { get; set; }        public DbSet<Tag> Tags { get; set; }        protected override void OnModelCreating(ModelBuilder modelBuilder)        {            // Blog与Post之间为 1 - N 关系
            modelBuilder.Entity<Post>()
                .HasOne(p => p.Blog)
                .WithMany(b => b.Posts)
                .HasForeignKey(p => p.BlogId)                // 使用HasConstraintName方法配置外键名称
                .HasConstraintName("ForeignKey_Post_Blog")
                .IsRequired();            // User与UserAccount之间为 1 - 1 关系
            modelBuilder.Entity<UserAccount>()
                .HasKey(c => c.UserAccountId);

            modelBuilder.Entity<UserAccount>()
                .HasOne(c => c.User)
                .WithOne(c => c.UserAccount)
                .HasForeignKey<UserAccount>(c => c.UserAccountId)
                .IsRequired();            // User与Blog之间为 1 - N 关系
            modelBuilder.Entity<Blog>()
                .HasOne(b => b.User)
                .WithMany(c => c.Blogs)
                .HasForeignKey(c => c.UserId)
                .IsRequired();            // Post与Tag之间为 N - N 关系
            modelBuilder.Entity<PostTag>()
                .HasKey(t => new {t.PostId, t.TagId});

            modelBuilder.Entity<PostTag>()
                .HasOne(pt => pt.Post)
                .WithMany(p => p.PostTags)
                .HasForeignKey(pt => pt.PostId)
                .IsRequired();

            modelBuilder.Entity<PostTag>()
                .HasOne(pt => pt.Tag)
                .WithMany(t => t.PostTags)
                .HasForeignKey(pt => pt.TagId)
                .IsRequired();
        }
    }    public class Blog
    {        public int BlogId { get; set; }        public string Url { get; set; }        public List<Post> Posts { get; set; }        public int UserId { get; set; }        public User User { get; set; }
    }    public class User
    {        public int UserId { get; set; }        public string UserName { get; set; }        public List<Blog> Blogs { get; set; }        public UserAccount UserAccount { get; set; }
    }    public class UserAccount
    {        public int UserAccountId { get; set; }        public string UserAccountName { get; set; }        public bool IsValid { get; set; }        public User User { 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 List<PostTag> PostTags { get; set; }
    }    public class Tag
    {        public string TagId { get; set; }        public List<PostTag> PostTags { get; set; }
    }    public class PostTag
    {        public int PostId { get; set; }        public Post Post { get; set; }        public string TagId { get; set; }        public Tag Tag { get; set; }
    }

14. Console中的EntityframeworkCore(2017年7月21日新增)

工作中时常会用到一些简单的EF场景,使用Console是最方便不过了,所以特此记录下。

  • 新建一个APS.NET CORE WEB模板项目

  • 安装相关Nuget包

    //Sql Server Database Provider    Install-Package Microsoft.EntityFrameworkCore.SqlServer
    
    //提供熟悉的Add-Migration,Update-Database等Powershell命令,不区分关系型数据库类型    Install-Package Microsoft.EntityFrameworkCore.Tools
  • 自定义Context

public class MyContext : DbContext{    protected override void OnModelCreating(ModelBuilder modelBuilder)    {
    }
}

注意我们删除了 public MyContext(DbContextOptions<MyContext> options) : base(options) 构造方法,以便我们可以直接 using(var context = new MyContext()) 的方法。简单来说,当你有依赖注入的需求时,便需要使用第一种构造模型。

  • 添加中文解码和 Configuration Nuget包

Install-Package System.Text.Encoding.CodePagesInstall-Package Microsoft.Extensions.Configuration.JsonInstall-Package Microsoft.Extensions.Configuration.EnvironmentVariables

// 需要新增appsettings.json文件,并添加ConnectionStrings节点,用于存放连接字符串。"ConnectionStrings": {    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=Console4;Trusted_Connection=True;MultipleActiveResultSets=true;"
  },
  • 将以下代码添加到MyContext中

private static IConfigurationRoot Configuration { get; set; }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){    //.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), @"..\.."))

    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json")
        .AddEnvironmentVariables();

    Configuration = builder.Build();

    optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
}
  • 在powershell中定位到MyContext所在文件夹运行相关迁移命令,要注意的是,appsettings文件也需要放到此文件夹中

Add-Migration InitializeUpdate-Database

之后便可以在Console中使用形如 using (var context = new MyContext())的语法对数据库进行操作了。

15. 参考链接和优秀博客

  1. EF CORE OFFICIAL DOC

  2. Introduction to Entity Framework

  3. Feature Comparison

  4. Entity Framework教程(第二版)


本文责任编辑: 加入会员收藏夹 点此参与评论>>
复制本网址-发给QQ/微信上的朋友
AI智能听书
选取音色