Entity Framework管理一对一实体关系

我们现在已经知道如何使用Code First来定义简单的领域类,并且如何使用DbContext类来执行数据库操作。现在我们来看下数据库理论中的多样性关系,我们会使用Code First来实现下面的几种关系:

  • 1、一对一关系: one to one
  • 2、一对多关系: one to many
  • 3、多对多关系::many to many

首先要明确关系的概念。关系就是定义两个或多个对象之间是如何关联的。它是由关系两端的多样性值识别的,比如,一对多意味着在关系的一端,只有一个实体,我们有时称为父母;在关系的另一端,可能有多个实体,有时称为孩子。EF API将那些端分别称为主体和依赖。一对多关系也叫做一或零对多(One-or-Zero-to-Many),这意味着一个孩子可能有或可能没有父母。一对一关系也稍微有些变化,就是关系的两端都是可选的。

一、EF里的实体关系配置

Has方法

With方法

配置实体关系:

一对一表关系设计:

一对一关系并不常用,但是偶尔也会出现。如果一个实体有一些可选的数据,那么你可以选择这种设计。

二、使用数据注解配置一对一关系

示例中Person表作为主表,IDCard表作为从表。

1、新建实体类

Person实体类结构如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 配置一对一实体关系.Model
{
    /// <summary>
    /// 主表
    /// </summary>
    [Table("Person")]
    public class Person
    {
        [Key]
        public int PersonId { get; set; }

        public string Name { get; set; }

        public int Sex { get; set; }

        public int Age { get; set; }

        /// <summary>
        /// virtual 表示是导航属性 启用贪懒加载
        /// </summary>
        [ForeignKey("PersonId")]
        public virtual IDCard IDCard { get; set; }
    }
}

IDCard实体类结构如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 配置一对一实体关系.Model
{
    public class IDCard
    {
        [Key]
        public int PersonId { get; set; }
        public string IDCardNo { get; set; }

        public DateTime DataIssue { get; set; }

        public DateTime ValidTime { get; set; }

        public string IssuingAuthority { get; set; }

        /// <summary>
        /// 导航属性
        /// </summary>
        public virtual Person Person { get; set; }
    }
}

2、创建EF数据上下文类

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 配置一对一实体关系.Model;

namespace 配置一对一实体关系.EF
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=CodeFirstApplication")
        {

        }

        public DbSet<Person> Persons { get; set; }

        public DbSet<IDCard> IDCards { get; set; }
    }
}

3、使用数据迁移的方式创建数据库

在使用数据迁移的过程中报错:Unable to determine the principal end of an association between the types '配置一对一实体关系.Model.Person' and '配置一对一实体关系.Model.IDCard'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations。通过查找资料,解决办法如下:添加Required数据注解,修改后的Person类结构如下:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 配置一对一实体关系.Model
{
    /// <summary>
    /// 主表
    /// </summary>
    [Table("Person")]
    public class Person
    {
        [Key]
        public int PersonId { get; set; }

        public string Name { get; set; }

        public int Sex { get; set; }

        public int Age { get; set; }

        /// <summary>
        /// virtual 表示是导航属性 启用贪懒加载
        /// </summary>
        [ForeignKey("PersonId")]
        [Required]
        public virtual IDCard IDCard { get; set; }
    }
}

4、查看生成的数据库表结构

通过查看数据库表结构,发现Person表和IDCards表建立了主外键关系。

总结:使用数据注解配置一对一关系的步骤:

1、使用数据注解Key标识主键。
2、两个实体之间的主键Key必须相同。
3、两个实体之间有相互引用的导航属性(使用virtual)。
4、在主表中设置外键关系[ForeignKey("PersonId")]。

三、使用Fluent API来配置一对一关系

1、创建A实体类结构如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 使用FluentAPI实现.Model
{
    public class A
    {
        public int AId { get; set; }
        public string Name { get; set; }
        public virtual B B { get; set; }
    }
}

2、创建B实体类结构如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 使用FluentAPI实现.Model
{
    public class B
    {
        public int AId { get; set; }
        public string Name { get; set; }
        public virtual A A { get; set; }
    }
}

3、使用Fluent API实现

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 使用FluentAPI实现.Model;

namespace 使用FluentAPI实现.EF
{
    public class EFDbContext :DbContext
    {
        public EFDbContext()
            : base("name=CodeFirstApplication")
        { }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IDCard>().ToTable("IDCard").HasKey(p => p.PersonId);
            modelBuilder.Entity<Person>().ToTable("Person").HasKey(p => p.PersonId);

            #region 1.0 默认一对一配置
            //modelBuilder.Entity<Person>().HasRequired(p => p.IDCard).WithOptional();

            #endregion

            #region 2.0 指定谁是主体对象谁是依赖对象
            // 指定当前Person对象依赖IDCard对象,外键会创建到IDCard对象中,而IDCard对象是独立存在的表,这种依赖关系配置是错误的。
            //modelBuilder.Entity<Person>().HasRequired(p => p.IDCard).WithRequiredDependent(t => t.Person);
            // 正确的依赖配置如下:
            //modelBuilder.Entity<IDCard>().HasRequired(p => p.Person).WithRequiredDependent(t => t.IDCard);
            //modelBuilder.Entity<A>().HasRequired(p => p.B).WithRequiredDependent(d=>d.A);//WithRequiredDependent A依赖对象(A依赖B,B可以对立存在,A会被建立外键)

            #endregion   指定谁是主要的对象
            modelBuilder.Entity<IDCard>().HasRequired(p => p.Person).WithRequiredPrincipal(t => t.IDCard);
            //WithRequiredPrincipal A 主体对象,执行A对象为被继承者,也就是父级,B继承A,A独立存在
            //modelBuilder.Entity<A>().HasRequired(p => p.B).WithRequiredPrincipal(d => d.A);
            #region MyRegion

            #endregion
            base.OnModelCreating(modelBuilder);
        }
    }
}

这里使用了HasKey方法,指定了一个表的主键,换言之,这是一个允许我们找到一个实体的独一无二的值。之前我们没有用这个方法是因为我们要么用了Key特性或者遵守了EF的默认约定(如果属性名是由类名加上"Id"后缀或者只是"Id"组成,那么EF会计算出该主键)。因为我们现在使用了PersonId作为主键,所以我们现在需要给运行时提供额外的提示,这就是HasKey派生用场的地方。最后子表中的主键会成为父表中的外键。

因为该关系是可选的,所以它也称为一或零对一(One-or-Zero-to-One)。关系的两端都是必须要存在的关系称为一对一。比如,每个人必须要有一个单独的login,这是强制性的。你也可以使用WithRequiredDepentent或者WithRequiredPrincipal方法来代替WithOptional方法。

注意:我们可以总是从该关系的主体端或者依赖端来配置关系。我们总是需要配置一对一关系的两端(即两个实体),使用Has和With方法确保一对一关系的创建。

到此这篇关于Entity Framework管理一对一实体关系的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Entity Framework实体拆分多个表

    一.概念 实体拆分:一个实体拆分成多个表,如Product实体,可以拆分成Product和ProductWebInfo两个表,Product表用于存储商品的字符类信息,ProductWebInfo用于存储商品的图片信息,两张表通过SKU进行关联. 1.Product实体类结构: using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Comp

  • Entity Framework管理一对一实体关系

    我们现在已经知道如何使用Code First来定义简单的领域类,并且如何使用DbContext类来执行数据库操作.现在我们来看下数据库理论中的多样性关系,我们会使用Code First来实现下面的几种关系: 1.一对一关系: one to one 2.一对多关系: one to many 3.多对多关系::many to many 首先要明确关系的概念.关系就是定义两个或多个对象之间是如何关联的.它是由关系两端的多样性值识别的,比如,一对多意味着在关系的一端,只有一个实体,我们有时称为父母:在关

  • Entity Framework管理一对二实体关系

    在上一篇文章中,简单的介绍了使用Fluent API如何管理一对一的实体关系,在这篇文章中,接着介绍Fluent API如何管理一对多的实体关系. 要在数据库中配置一对多关系,我们可以依赖EF约定,还可以使用数据注解或Fluent API来显式创建关系.接下来使用捐赠者Donator和支付方法PayWay这两个类来举例子,这里的一对多关系是:一个人可以通过多种支付方式赞助我. 支付方式类PayWay结构如下: using System; using System.Collections.Gene

  • Entity Framework管理并发

    理解并发 并发管理解决的是允许多个实体同时更新,实际上这意味着允许多个用户同时在相同的数据上执行多个数据库操作.并发是在一个数据库上管理多个操作的一种方式,同时遵守了数据库操作的ACID属性(原子性.一致性.隔离性和持久性). 想象一下下面几种可能发生并发的场景: 1.用户甲和乙都尝试修改相同的实体. 2.用户甲和乙都尝试删除相同的实体. 3.用户甲正在尝试修改一个实体时,用户乙已经删除了该实体. 4.用户甲已经请求读取一个实体,用户乙读完该实体之后更新了它. 这些场景可能会潜在地产生错误的数据

  • Entity Framework使用Code First的实体继承模式

    目录 一.TPT继承模式 1.Person类 2.使用数据迁移创建数据库 3.填充数据 二.TPH模式 1.创建有继承关系的实体类 2.创建数据上下文 3.使用数据迁移创建数据库 4.不使用默认生成的区别多张表的类型 5.填充数据 6.查询数据 三.TPC模式 1.创建实体类 2.配置数据上下文 3.使用数据迁移生成数据库 4.填充数据 Entity Framework的Code First模式有三种实体继承模式 1.Table per Type (TPT)继承 2.Table per Clas

  • Entity Framework使用Code First模式管理视图

    一.什么是视图 视图在RDBMS(关系型数据库管理系统)中扮演了一个重要的角色,它是将多个表的数据联结成一种看起来像是一张表的结构,但是没有提供持久化.因此,可以将视图看成是一个原生表数据顶层的一个抽象.例如,我们可以使用视图提供不同安全的级别,也可以简化必须编写的查询,尤其是我们可以在代码中的多个地方频繁地访问使用视图定义的数据.EF Code First模式现在还不完全支持视图,因此我们必须使用一种变通的方法.这种方法是:将视图真正看成是一张表,让EF定义这张表,然后在删除它,最后再创建一个

  • Entity Framework使用Code First模式管理存储过程

    在EF中使用存储过程和使用视图是很相似的,一般会使用Database对象上的两个方法:SqlQuery和ExecuteSqlCommand.为了从存储过程中读取很多数据行,我们只需要定义一个类,我们会将检索到的所有数据行物质化到该类实例的集合中.比如,从下面的存储过程读取数据: CREATE PROCEDURE [dbo].[SelectBooks] @BookTypeName AS NVARCHAR(10) AS BEGIN select B.Name,B.Author,B.Publicati

  • Entity Framework使用Code First模式管理事务

    一.什么是事务 处理以数据为中心的应用时,另一个重要的话题是事务管理.ADO.NET为事务管理提供了一个非常干净和有效的API.因为EF运行在ADO.NET之上,所以EF可以使用ADO.NET的事务管理功能. 当从数据库角度谈论事务时,它意味着一系列操作被当作一个不可分割的操作.所有的操作要么全部成功,要么全部失败.事务的概念是一个可靠的工作单元,事务中的所有数据库操作应该被看作是一个工作单元. 从应用程序的角度来看,如果我们有多个数据库操作被当作一个工作单元,那么应该将这些操作包裹在一个事务中

  • Entity Framework模型优先与实体对象查询

    目录 一.概念: 1.EF6 可实现的功能: 二.安装Entity Framework6 1.VS2019 Installer安装“Entity Framework6 工具” 2.通过Nuget安装“Entity Framework”: 三.新建ObjectContext 和EntityObject 1.“根据模型生成的数据库”的设计器功能实现ModelFirst. 四.实体对象查询:linq to Entities 1.使用lambda表达式查询 2.执行存储过程(通过“添加”-----”函数

  • Entity Framework配置关系

    目录 一.Has方法与With方法 1.Has方法: 2.With方法: 二.一对一关系: 1.DataAnnotations数据标注的方式 2.Fluent API方式 (1)1:0..1关系 (2)1:1 关系 三.一对多关系: 1.DataAnnotations方式 2.更改外键的nullable属性和外键的名字 1.使用Data Annotations指定外键: 2.用Fluent API指定外键: 3.对同一实体多个引用的情况 4.级联删除 四.多对多关系 指定表名 一.Has方法与W

  • Entity Framework使用Code First模式管理数据库

    一.管理数据库连接 1.使用配置文件管理连接之约定 在数据库上下文类中,如果我们只继承了无参数的DbContext,并且在配置文件中创建了和数据库上下文类同名的连接字符串,那么EF会使用该连接字符串自动计算出数据库的位置和数据库名.比如,我们的数据库上下文定义如下: using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; usin

随机推荐