【EF Core】实体的主、从关系

这篇具有很好参考价值的文章主要介绍了【EF Core】实体的主、从关系。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

假设有以下两个实体:

public class Student
{
    public int StuID { get; set; }
    public string? Name { get; set; }
    public IEnumerable<Homework>? Homeworks { get; set; }
}

public class Homework
{
    public string? Class { get; set; }
    public string? Subject { get; set; }
}

Homework 类表示家庭作业,它并不是独立使用的,而是与学生类(Student)有依赖关系。一位学生有多个家庭作业记录,即 Homework 对象用于记录每位同学的作业的。按照这样的前提,Student 是主对象,Homework 是从对象。

Student 对象有个 Homeworks 属性,用于引用 Homework 对象,也就是所谓的“导航属性”。这个“导航”,估计意思就是你通过这个属性可以找到被引用的另一个实体对象,所以称之为导航,就是从 Navigation 的翻译。

随后,咱们要从 DbContext 类派生出自定义的数据库上下文。

public class MyDbContext : DbContext
{
    // 映射的数据表,名称默认与属性名称一样
    // 即 Students + Works
    public DbSet<Student> Students => Set<Student>();
    public DbSet<Homework> Works => Set<Homework>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // 设置连接字符串
        optionsBuilder.UseSqlServer(Helper.Conn_STRING);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 设置主键
        modelBuilder.Entity<Student>().HasKey(s => s.StuID);
        // 建立主从关系
        modelBuilder.Entity<Student>().OwnsMany(s => s.Homeworks);
    }
}

连接字符串是老周事先配置好的,连的是 SQL Server。

public class Helper
{
    public const string Conn_STRING = "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=stuDB;Integrated Security=True";
}

用的是 LocalDB,这玩意儿方便。

其实这是一个控制台应用程序,并添加了 Nuget 包。

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.8" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.8" />
  </ItemGroup>

好,回到咱们的代码中,MyDbContext 重写了两个方法:

1、重写 OnConfiguring 方法,做一些与该 Context 有关的配置,通常是配置连接字符串;也可能配置一下日志输出。上面代码中使用的是扩展方法 UseSqlServer。这就是引用 Microsoft.EntityFrameworkCore.SqlServer Nuget 包的作用。

2、重写 OnModelCreating 方法。这个是设置实体类相关的模型属性,以及与数据表的映射,或配置实体之间的关系。上述代码中,老周做了两件事:A、为 Student 实体设置主键,作为主键的属性是 StuID;B、建立 Student 和 Homework 对象的主从关系,调用 OwnsMany 方法的意思是:一条 Student 记录对应 N 条 Homework 记录。因为 Student 类的 Homeworks 属性是集合。

在 Main 方法中,咱们要做两件事:A、根据上面的建模创建数据库;B、往数据库中存一点数据。

static void Main(string[] args)
{
    using (var ctx = new MyDbContext())
    {
        //ctx.Database.EnsureDeleted();
        bool res = ctx.Database.EnsureCreated();
        if (res)
        {
            Console.WriteLine("已创建数据库");
        }
    }

    using(MyDbContext ctx = new())
    {
        // 加点料
        ctx.Students.Add(new Student
        {
            Name = "小张",
            Homeworks = new List<Homework>
            {
                new Homework{ Class = "数学", Subject = "3000道口算题"},
                new Homework{ Class = "英语", Subject = "背9999个单词"}
            }
        });

        ctx.Students.Add(new Student
        {
            Name = "小雪",
            Homeworks = new Homework[]
            {
                new Homework{ Class = "历史", Subject = "临一幅《清明上河图》"},
                new Homework{ Class = "语文", Subject = "作文题:《百鬼日行》"}
            }
        });

        // 保存
        int x = ctx.SaveChanges();
        Console.WriteLine("共保存了{0}条记录", x);
    }
}

EnsureCreated 方法会自动创建数据库。如果不存在数据库且创建成功,返回 true,否则是 false。数据库的名称在连接字符串中配置过。

Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=stuDB;Integrated Security=True

接下来,我们运行一下。稍等几秒钟,看到控制台输出下面文本就算成功了。

已创建数据库
共保存了6条记录

然后,连上去看看有没有数据库。

【EF Core】实体的主、从关系

看看,这表的名称是不是和 MyDbContext 的两个属性一样? 

public class MyDbContext : DbContext
{
    public DbSet<Student> Students => Set<Student>();
    public DbSet<Homework> Works => Set<Homework>();
    ……

你要是不喜欢用这俩名字,也可以发动传统技能(指老 EF),用 Table 特性给它们另取高名。

[Table("tb_students", Schema = "dbo")]
public class Student
{
   ……
}

[Table("tb_homeworks", Schema = "dbo")]
public class Homework
{
    ……
}

删除数据库,再运行一次程序,然后再登录数据库看看,表名变了吗?

【EF Core】实体的主、从关系

那有伙伴们会问:有没有现代技能?有的,使用 ToTable 方法定义映射的数据表名称。

先去掉 Student、Homework 类上的 Table 特性,然后直接在重写 OnModelCreating 方法时配置。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().ToTable("dt_students").HasKey(s => s.StuID);
    modelBuilder.Entity<Homework>().ToTable("dt_works");
    // 建立主从关系
    modelBuilder.Entity<Student>().OwnsMany(s => s.Homeworks);
}

但是这样写会报错的。因为 Homework 实体是 Student 的从属对象,单独调用 ToTable 方法在配置的时候会将其设置为独立对象,而非从属对象。

所以,正确的做法是在两个实体建立了从属性关系后再调用 ToTable 方法(Student 对象是主对象,它可以单独调用)。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().HasKey(s => s.StuID);
    modelBuilder.Entity<Student>()
        .ToTable("tb_students")
        .OwnsMany(s => s.Homeworks)
        .ToTable("tb_works");
}

因为 Homework 是 Student 的从属,tb_works 表中要存在一个外键——引用 Student.StuID,这样两个表才能建立主从关系。如果单独调用 Entity<Homework>.ToTable 映射表的话,那么表中不会添加引用 StuID 的外键列。就是默认被配置为非主从模式。没有了外键,tb_works 表中存的数据就无法知道是哪位学生的作业了。

这样创建数据库后,tb_works 表中就存在名为 StudentStuID 的列,它就是引用 Student.StuID 的外键。

CREATE TABLE [dbo].[tb_works] (
    [StudentStuID] INT            NOT NULL,
    [Id]           INT            IDENTITY (1, 1) NOT NULL,
    [Class]        NVARCHAR (MAX) NULL,
    [Subject]      NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([StudentStuID] ASC, [Id] ASC),
    CONSTRAINT [FK_tb_works_tb_students_StudentStuID] FOREIGN KEY ([StudentStuID]) REFERENCES [dbo].[tb_students] ([StuID]) ON DELETE CASCADE
);

当然,这个外键名字是根据实体类名(Student)和它的主键属性名(StuID)生成的,如果你想自己搞个名字,也是可以的。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().HasKey(s => s.StuID);
    modelBuilder.Entity<Student>()
        .ToTable("tb_students")
        .OwnsMany(s => s.Homeworks, tb =>
        {
            tb.ToTable("tb_works");
            tb.WithOwner().HasForeignKey("student_id");
        });
}

这样 tb_works 表中就有了名为 student_id 的外键。

CREATE TABLE [dbo].[tb_works] (
    [student_id] INT            NOT NULL,
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [Class]      NVARCHAR (MAX) NULL,
    [Subject]    NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([student_id] ASC, [Id] ASC),
    CONSTRAINT [FK_tb_works_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([StuID]) ON DELETE CASCADE
);

OwnsXXX 方法是指:俺是主表,我要“关照”一下从表;

WithOwner 方法是指:俺是从表,我要配置一下和主表之间建立联系的参数(如上面给外键另起个名字)。

那么,我想把两个表的列全自定义命名,可以吗?当然可以的。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().HasKey(s => s.StuID);
    modelBuilder.Entity<Student>()
        .ToTable("tb_students", tb =>
        {
            tb.Property(s => s.StuID).HasColumnName("sID");
            tb.Property(s => s.Name).HasColumnName("stu_name");
        })
        .OwnsMany(s => s.Homeworks, tb =>
        {
            tb.ToTable("tb_works");
            tb.WithOwner().HasForeignKey("student_id");
            tb.Property(w => w.Class).HasColumnName("wk_class");
            tb.Property(w => w.Subject).HasColumnName("wk_sub");
        });
}

两个表的字段名都变了。

CREATE TABLE [dbo].[tb_students] (
    [sID]      INT            IDENTITY (1, 1) NOT NULL,
    [stu_name] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_tb_students] PRIMARY KEY CLUSTERED ([sID] ASC)
);

CREATE TABLE [dbo].[tb_works] (
    [student_id] INT            NOT NULL,
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [wk_class]   NVARCHAR (MAX) NULL,
    [wk_sub]     NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_tb_works] PRIMARY KEY CLUSTERED ([student_id] ASC, [Id] ASC),
    CONSTRAINT [FK_tb_works_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([sID]) ON DELETE CASCADE
);

注意:Homework 类中没有定义 Id 属性(主键),它是自动生成的。

 

有大伙伴会想,在 OnModelCreating 方法中建模我头有点晕,我能不能在定义实体类的时候,直接通过特性批注来实现主从关系呢?那肯定可以的了。

[Table("tb_students")]
[PrimaryKey(nameof(StuID))]
public class Student
{
    [Column("sID")]
    public int StuID { get; set; }

    [Column("st_name")]
    public string? Name { get; set; }

    // 这是导航属性,不需要映射到数据表
    public IEnumerable<Homework>? Homeworks { get; set; }
}

[Owned]
[Table("tb_homeworks")]
[PrimaryKey(nameof(wID))]
public class Homework
{
    [Column("wk_id")]
    public int wID { get; set; }

    [Column("wk_class")]
    public string? Class { get; set; }

    [Column("wk_sub")]
    public string? Subject { get; set; }

    [ForeignKey("student_id")]  //设置外键名称
    public Student? StudentObj { get; set; }
}

PrimaryKey 特性设置实体类中哪些属性为主键,使用属性成员的名称,而不是数据表字段名称。

在 Homework 类上用到 Owned 特性,表示其他对象如果引用了 Homework,就会自动建立主从关系—— Homework 为从属对象。

ForeignKey 特性指定外键的名称。虽然 StudentObj 属性的类型是 Student 类,但在建立数据表时,只引用了 Student 类的 StuID 属性。

此时,可以清空 OnModelCreating 方法中的代码了。

生成的数据表结构与上文差不多。

CREATE TABLE [dbo].[tb_students] (
    [sID]     INT            IDENTITY (1, 1) NOT NULL,
    [st_name] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_tb_students] PRIMARY KEY CLUSTERED ([sID] ASC)
);

CREATE TABLE [dbo].[tb_homeworks] (
    [wk_id]      INT            IDENTITY (1, 1) NOT NULL,
    [wk_class]   NVARCHAR (MAX) NULL,
    [wk_sub]     NVARCHAR (MAX) NULL,
    [student_id] INT            NULL,
    CONSTRAINT [PK_tb_homeworks] PRIMARY KEY CLUSTERED ([wk_id] ASC),
    CONSTRAINT [FK_tb_homeworks_tb_students_student_id] FOREIGN KEY ([student_id]) REFERENCES [dbo].[tb_students] ([sID])
);

当然了,最好的做法是将特性批注与 OnModelCreating  方法结合使用。文章来源地址https://www.toymoban.com/news/detail-514743.html

到了这里,关于【EF Core】实体的主、从关系的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .net 6 EF Core MySql数据库表生成实体类命令

    安装下面这几个包 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Tools Microsoft.EntityFrameworkCore.Design Pomelo.EntityFrameworkCore.MySql Scaffold-DbContext “server=127.0.0.1;port=3306;database=DB;uid=root;pwd=pwdpwd;sslmode=none;” Pomelo.EntityFrameworkCore.MySql -OutputDir Models -Force -NoOnConfiguring -NoPluralize -Context “D

    2024年02月05日
    浏览(34)
  • .NET6.0 EF Core 之 DB First生成实体类

    EF Core可以使用DB First模式生成实体类具体步骤如下: 因为.NET Core中默认不包含EF Core的工具和程序包,需要通过NuGet管理器安装对应的工具和程序包,这里使用SQL Server数据库。 Microsoft.EntityFrameworkCore.SqlServer:SQL Server数据库EF提供程序 Microsoft.EntityFrameworkCore.Design:设计时使用到

    2024年02月06日
    浏览(41)
  • .NET6 + EF Core + MySQL 创建实体和数据库、EFCore 数据迁移

    接上期文章《.NET6项目连接数据库方式方法》,有人问了我几个问题,现在就这几个问题,拓展延申一下创建实体类、数据库。把ORM框架和数据迁移都写进去。 我的项目是在Linux上创建的,使用的是vscode开发工具远程开发。为了方便大家阅读和操作,我将项目down到我的本地电

    2024年02月05日
    浏览(38)
  • 记录一次EF实体跟踪错误

    在我写文章编辑接口的,出现了一个实体跟踪的错误,详情如下 System.InvalidOperationException: The instance of entity type \\\'Tag\\\' cannot be tracked because another instance with the same key value for {\\\'Id\\\'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attach

    2024年02月11日
    浏览(28)
  • 使用EF6(DB First模式)无法生成对应模型实体类

    最近升级了,Visual Stidio 2022,在使用EF6时(DB First模式),无法生成对应模型的实体类,如下:  对于该问题,我去微软社区,找到了两个解决方案: 1.从 Visual Studio2022 16.x 版本回滚到 Visual Studio2022 15.x 版本即可解决问题; 2.修改EF6的实用程序.CS.ttinclude,它默认的位置在:C:Program FilesMicro

    2024年02月11日
    浏览(23)
  • EF Core入门

    EF Core是微软官方提供的ORM框架。EF Core不仅可以操作Microsoft SQL Server、MySQL、Oracle、PostgreSQL等数据库,而且可以操作Azure Cosmos DB等NoSQL数据库 前提条件:已经完整安装了Microsoft SQL Server 下面是一个实际操作EF Core的演示 这是项目最终的目录,这里需要关注的就是 .cs 文件 首先新

    2023年04月09日
    浏览(25)
  • 4.1EF Core

    EF Core是微软官方的ORM框架,ORM即对象关系映射,也就是我们可以直接操作C#中的对象就可以完成数据库的操作。 EF Core环境搭建 首先要创建C#对象,用以对应数据库中的表,该C#对象也成为实体类。 根据所用的数据库选择NuGet包,本文使用SQLite数据库,所以安装Microsoft.EntityF

    2024年02月05日
    浏览(29)
  • EF Core并发控制

    并发控制:避免多个用户同时操作资源造成的并发冲突问题。 最好的解决方案:非数据库解决方案 数据库层面的两种策略:悲观、乐观 悲观并发控制一般采用行锁 ,表锁等排他锁对资源进行锁定,确保同时只有一个使用者操作被锁定的资源。 EF Core没有封装悲观并发控制的

    2024年02月10日
    浏览(36)
  • Net Core中使用EF Core连接Mysql数据库

    Entity Framework Core的前身是微软提供并主推的ORM框架,简称EF,其底层是对ADO.NET的封装。EF支持SQLServer、MYSQL、Oracle、Sqlite等所有主流数据库。 首先是使用时的几个模式的整理及其理解: Code First:根据代码自动创建数据库表结构甚至是数据库,可以支持多库开发,代码较少冗余

    2024年01月24日
    浏览(41)
  • EF Core + MySQL 基本增删改查

    基于EF Core + MySQL的基本增删改查,示例是基于.NET6 + EF Core + MySQL 创建实体和数据库、EFCore 数据迁移项目基础上的内容增加。同时也是对基于Canal实现MySQL 8.0 数据库数据同步项目的验证。 Controllers----添加----控制器,选择api----包含读写操作的API控制器。 将上下文类注入到User

    2024年02月08日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包