4.Entity Framework Core关系

1.实体与属性

默认情况下,如果在类型上发现导航属性,将创建关系。

如果它指向的类型不能由当前数据库提供程序映射为标量类型,则属性被视为导航属性。

//Blog 是主体实体
public class Blog
{

    //Blog.BlogId 是主体键
    public int BlogId { get; set; }
    public string Url { get; set; }

    
    //Blog.Posts 是集合导航属性
    //Blog.Posts 是 Post.Blog的反向导航属性
    public List<Post> Posts { get; set; }
}

//Post 是依赖实体
public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    //Post.BlogId 是外键
    public int BlogId { get; set; }


    //Post.Blog 是引用导航属性
    //Post.Blog 是 Blog.Posts 的反向导航属性
    public Blog Blog { get; set; }
}

1.1主体实体

主体实体是指包含主体键/备选键属性的实体。有时被称为关系的“父级”。

1.2依赖实体

依赖实体是指包含外键属性的实体。有时被称为关系的“子级”。

1.3主体键属性

唯一标识主体实体的属性。它可能是主键,也可能是备选键。

1.4主键属性

默认情况下,EF 会将名为 ID 或 ClassNameID(当前实体的名称+ID) 的属性识别为主键。

1.5外键属性

默认情况下,主体键为ID名称时,EF 会将名为<导航属性名称><主体键属性名称>的属性识别为外键。

主键为ClassNameID名称时,EF 会将名为<主体键属性名称>的属性识别为外键。

1.6集合导航属性

集合导航属性是指包含对许多相关实体引用的导航属性。

集合导航属性类型必须为列表,如 ICollection<T>List<T>HashSet<T>

1.7引用导航属性

引用导航属性是指包含单个实体的引用导航属性。

1.8反向导航属性

反向导航属性是指当前导航属性关系上另一端上的导航属性。

1.9必需属性

如果 null 不是要分配给属性的有效值,则它被视为必需属性。

映射到关系型数据库架构时,必需属性创建为不可为 null 的列。

1.10可选属性

如果属性包含 null 是有效的,则该属性被视为可选属性。

映射到关系型数据库架构时,可选属性创建为可为 null 的列。

2.一对多

2.1两端导航属性和外键属性

//Blog主体实体中的集合导航属性
public List<Post> Posts { get; set; }
//Post依赖实体中的外键属性和引用导航属性
public int BlogId { get; set; }
public Blog Blog { get; set; }

2.2两端导航属性(省略外键属性)

如果拥有相关实体的导航属性,则 Entity Framework 不会要求为数据模型添加外键属性。只要有需要,EF 就会自动在数据库中创建外键,并为其创建影子属性(shadow properties)。但如果数据模型包含外键,则会使更新变得更简单、更高效。

//Blog主体实体中的集合导航属性
public List<Post> Posts { get; set; }
//Post依赖实体中的外键属性(可省略)和引用导航属性
public int BlogId { get; set; }
public Blog Blog { get; set; }

2.3单个导航属性

仅包含一个导航属性(没有反向导航,也没有外键属性)就足以按约定定义关系。当然,还可以包含一个导航属性和一个外键属性这种组合来定义关系。

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; }
}

3.一对一

一对一关系在两端都有引用导航属性,且有外键属性。

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; }
}

4.多对多

“带有效负载”是指 中间联接表 包含除联接表外键之外的其它属性(比如为主键属性 和 其它属性)。

4.1显式中间联接表(带有效负载或不带有效负载)

多对多关系是实际上是通过两个一对多关系实现的,即通过一个中间联接表,关联两个一对多关系,就形成了多对多关系。

Student 和 Course 实体间存在多对多关系,Enrollment 实体在数据库中充当带有效负载的多对多联接表。

//Student实体中的导航属性
public ICollection<Enrollment> Enrollments { get; set; }
//外键
public int StudentID { get; set; }
public int CourseID { get; set; }

//Enrollment实体中与外键相关联的的导航属性
public Student Student { get; set; }
public Course Course { get; set; }
//Course实体中的导航属性
public ICollection<Enrollment> Enrollments { get; set; }

4.2隐式中间联接表(不带有效负载)

多对多关系需要双方的集合导航属性。

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 class Tag
{
    public string TagId { get; set; }

    public ICollection<Post> Posts { get; set; }
}

5.级联删除

如果实体类中具有外键属性,则关系的必需性取决于外键属性是必需属性还是可选属性。

对于必需关系,级联删除将设置为 Cascade,表示也会删除依赖实体。

对于可选关系,它将设置为 ClientSetNull,表示未加载到内存中的依赖实体将保持不变,必须 手动删除 或 更新以指向有效的主体实体。对于加载到内存中的实体,EF Core 将尝试将外键属性设置为 null。

6.一对多(Fluent API)

如果确实要混合使用 特性和 Fluent API,请注意,只要出现冲突,Fluent API 就会覆盖特性。

首先,应使用 HasOne 或 HasMany 标识导航属性,然后,使用 WithOne 或 WithMany 以标识反向导航属性。HasMany/WithMany 用于集合导航属性,HasOne/WithOne 用于引用导航属性。

6.1两端导航属性

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 Blog Blog { 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);
    }
}
//方式二
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>()
            .HasMany(b => b.Posts)
            .WithOne(p => p.Blog);
    }
}

6.2单个导航属性

如果只有一个导航属性,则 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>()
            .HasMany(b => b.Posts)
            .WithOne();
    }
}

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; }
}

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/entityframeworkcore/15395.html

(0)
上一篇 2022年6月3日 23:42
下一篇 2022年6月4日 22:51

相关推荐

发表回复

登录后才能评论