Entity Framework 6 Code First 实践系列(1):实体类配置总结

EF实体类的配置可以使用 数据注释或 Fluent API两种方式配置,Fluent API 配置的关键在于搞清实体类的依赖关系,按此方法配置,快速高效合理。为了方便理解,我们使用简化的实体A和B以及A、B的配置类AMap和BMap,来演示如何正确配置实体类关系的过程。

public class A
{
	public int Id { get; set; }
}

public class B
{
	public int Id { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
	public AMap()
	{
		this.HasKey(o => o.Id);
	}
}

public class BMap : EntityTypeConfiguration<B>
{
	public BMap()
	{
		this.HasKey(o => o.Id);
	}
}

实体类配置

一、确定依赖关系:

假设实体B依赖于实体A(B->A),那么实体B中存在对实体A的引用。

二、实体类配置应该写在哪里?

假设B依赖于A(B->A),很显然,我们希望的是B表中生成外键(A表的主键值)。以下两种方式都可以实现相同的表结构,但毫无疑问我们应该在B的配置文件BMap中进行关系配置。

(1)B依赖于A,A可以对B的存在一无所知。

(2)A可以单独存在,配置写在哪里都不会对A表产生影响。

(3)B对A的依赖是通过在B表中生成外键(A表的主键)。

推荐的写法:

public class BMap : EntityTypeConfiguration<B> { public BMap() { this.HasRequired(o => o.A).WithMany(o=>o.ListB); } }

摒弃的写法 :

public class AMap : EntityTypeConfiguration<A> { public AMap() { this.HasMany(o => o.ListB).HasRequired(o => o.A); } }

依赖的方向决定了使用的配置,这在实体类数量和关系复杂时尤其重要,假设有10个实体类依赖A,混合书写配置显然不可取,而在被依赖实体中配置的结果会导致经常修改A的配置文件,你甚至不肯定修改了类A的配置文件是会引起A表的变化。

三、配置依赖关系

配置文件的基类 EntityTypeConfiguration包含了一系列Has方法用来配置实体类,其中HasOptional和HasRequired根据实体的引用属性配置实体关系。假设B依赖于A(B->A),HasOptional允许B单独存在,这将在B表中生成可空的外键。HasRequired不允许B单独存在,这将在B表中生成非空的外键。

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A);
    }
}

HasRequired

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A);
    }
}

HasOptional

四、配置关联类型

HasOptional和HasRequired分别返回OptionalNavigationPropertyConfiguration和 RequiredNavigationPropertyConfiguration对象,我们使用其中的WithMany和WithOptional来配置关联的类型。

如果A:B = 1:N,我们使用 WithMany。

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany();
    }
}

1:N(外键可空)

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A).WithMany();
    }
}

1:N(外键不可空)

如果A:B= 1:1,我们使用 WithOptional。1:1的关联要求外键的非空和唯一,数据库是通过表B的外键作为主键来实现 。

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasRequired(o => o.A).WithOptional();
    }
}

1:1

四、可选导航属性

导航属性由关联类型决定,但其存在与否不会影响实体的依赖关系和关联类型。

对于B->A,如果A:B = 1:N,我们可以在A中添加ICollection<B>类型的导航属性,同时修改关系配置,将该属性传递给WithMany方法。

public class A
{
	public int Id { get; set; }

	public ICollection<B> BList { get; set; }
}

public class B
{
	public int Id { get; set; }

	public A A { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
	public AMap()
	{
		this.HasKey(o => o.Id);
	}
}

public class BMap : EntityTypeConfiguration<B>
{
	public BMap()
	{
		this.HasKey(o => o.Id);
		this.HasOptional(o => o.A).WithMany(o => o.BList);
	}
}

导航属性

如果A:B = 1:1,我们可以在A中添加B类型的导航属性,同时修改关系配置,将该属性传递给WithOptional方法。

public class A
{
	public int Id { get; set; }

	public B B { get; set; }
}

public class B
{
	public int Id { get; set; }

	public A A { get; set; }
}

public class AMap : EntityTypeConfiguration<A>
{
	public AMap()
	{
		this.HasKey(o => o.Id);
	}
}

public class BMap : EntityTypeConfiguration<B>
{
	public BMap()
	{
		this.HasKey(o => o.Id);
		this.HasRequired(o => o.A).WithOptional(o => o.B);
	}
}

导航属性

五、显式外键属性

对于B->A,如果A:B = 1:1,外键就是主键。

如果A:B = 1:N,我们可以自定义导航属性对应的外键属性,首先在B中添加显式的用于外键的属性。

public class B
{
    public int Id { get; set; }

    public A A { get; set; }

    //public int AId { get; set; }
    public int? AId { get; set; }
}

显式外键

WithMany返回DependentNavigationPropertyConfiguration对象,我们使用该对象的HasForeignKey方法,如果实体联系配置为HasOptional ,则需要使用可空类型匹配。

public class BMap : EntityTypeConfiguration<B>
{
    public BMap()
    {
        this.HasKey(o => o.Id);
        this.HasOptional(o => o.A).WithMany().HasForeignKey(o => o.AId);
    }
}

外键配置

六、级联删除配置

HasForeignKey返回 CascadableNavigationPropertyConfiguration对象,EF 默认开启级联删除,当实体关系复杂导致无法开启级联删除时,我们使用该对象的WillCascadeOnDelete方法配置取消级联删除。

七、关于双向依赖

EF中实体的关联通过表的外键实现,1:N还是1:1都是通过外键实现。我们可以根据1:N配置的方式配置出双向依赖的表,但通常所谓的多对多都不是双向依赖。例如用户和角色、学生和课程、文章和标签等,甚至根本没有依赖,因为二者都可以独立存在,有的只是映射关系对二者的依赖,而这是1:N的问题。

我们使用EntityTypeConfiguration配置实体依赖,该类的ToTable、HasKey等实例方法都用于配置当前实体类映射的Table。HasRequired和HasOptional方法也会在对应的Table中生存外键,而HasMany方法则是其中的异类,偏偏配置的非当前实体类。

HasMany、WithMany除了在配置双向引用时替我们自动生成关系表,带来更多的是配置混乱。而所谓的自动生成关系表更是打破了我们实体类和Table的一一对应。在Microsoft.AspNet.Identity.EntityFramework 1.0中,我们可以看到IdentityUser和IdentityRole并没有通过双向引用自动生成关系表,而是定义了IdentityUserRole实体类用来映射: 通过IdentityDbContext<TUser>的 OnModelCreating配置 我们可以看到虽然使用了HasMany配置TUser的Roles属性,但是完全可以在IdentityUserRole中配置。即使在2.0版本中依旧如此。

 、常见的配置举例:

1.用户和角色:

(1)确定依赖关系:User和Role都可以单独存在,但UserRole不可以单独存在,因此存在的依赖是UserRole->User,UserRole->Role。

(2)配置依赖关系:UserRole不能单独存在,因此使用 HasRequired。

(3)确定关联类型:User:UserRole==1:*;Role:UserRole=1:*,因此使用 WithMany。

(4) 显式的外键属性:在UserRole中添加UserId和RoleId作为显式的外键属性。

(5)可选的导航属性:在User和Role中添加ICollection<UserRole>类型的导航属性。

UserRole不应该存在重复的用户角色映射,因此使用外键作为联合主键。

public class User
{
	public User()
	{
		this.UserRoles = new List<UserRole>();
	}

	public int Id { get; set; }

	public string UserName { get; set; }

	public ICollection<UserRole> UserRoles { get; set; }
}

public class Role
{
	public Role()
	{
		this.UserRoles = new List<UserRole>();
	}

	public int Id { get; set; }

	public string RoleName { get; set; }

	public ICollection<UserRole> UserRoles { get; set; }
}

public class UserRole
{
	public User User { get; set; }

	public int UserId { get; set; }

	public Role Role { get; set; }

	public int RoleId { get; set; }
}

public class UserRoleMap : EntityTypeConfiguration<UserRole>
{
	public UserRoleMap()
	{
		this.HasKey(o => new { o.UserId, o.RoleId });
		this.HasRequired(o => o.User).WithMany(o => o.UserRoles).HasForeignKey(o => o.RoleId);
		this.HasRequired(o => o.Role).WithMany(o => o.UserRoles).HasForeignKey(o => o.UserId);
	}
}

UserRole

2.节点树:

(1)确定依赖关系:Category自依赖,Category->Category

(2)配置依赖关系:Category可以单独存在,因此使用HasOptional。

(3)确定关联类型:Category:Category==1:*, 因此使用 WithMany。

(4)显式的外键属性:在UserRole中添加ParentId,由于Category可以单独存在,ParentId为可空类型。

(5)可选的导航属性:在Category中添加ICollection<Category>类型的导航属性。

public class Category
{
	public int Id { get; set; }

	public string Name { get; set; }

	public int? ParentId { get; set; }

	public Category Parent { get; set; }

	public ICollection<Category> Children { get; set; }
}

public class CategoryMap : EntityTypeConfiguration<Category>
{
	public CategoryMap()
	{
		this.HasKey(o => o.Id);
		this.HasOptional(o => o.Parent).WithMany(o => o.Children).HasForeignKey(o => o.ParentId);
	}
}

Category->Category

时间: 2024-10-13 11:14:25

Entity Framework 6 Code First 实践系列(1):实体类配置总结的相关文章

【转】Entity Framework 6 Code First 实践系列(1):实体类配置-根据依赖配置关系和关联

本文转自:http://www.cnblogs.com/easygame/p/3622893.html EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关键在于搞清实体类的依赖关系,按此方法配置,快速高效合理.为了方便理解,我们使用简化的实体A和B以及A.B的配置类AMap和BMap,来演示如何正确配置实体类关系的过程. public class A { public int Id { get; set; } } public class B { p

创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添加一个User类: namespace MyFirstApp.Models { public class User { public int ID { get; set; } public string Name { get; set; } public string Email { get; se

1.Relationship in Entity Framework Using Code First Approach With Fluent API【使用EF Code-First方式和Fluent API来探讨EF中的关系】

In this article, you will learn about relationships in Entity Framework using the Code First Approach with Fluent API. 在这篇文章中,你将会学习到使用EF Code-First方式和Fluent API来探讨EF中的关系(一对一,一对多,多对多). Introduction[介绍] A relationship, in the context of databases, is a

MVC2、MVC3、MVC4、MVC5之间的区别 以及Entity Framework 6 Code First using MVC 5官方介绍教程

现在MVC的技术日趋成熟,面对着不同版本的MVC大家不免有所迷惑 -- 它们之间有什么不同呢?下面我把我搜集的信息汇总一下,以便大家能更好的认识不同版本MVC的功能,也便于自己查阅. View Engine : View Engine is responsible for rendering of the HTML code from your views to the browser.MVC 2 uses only Web Forms view engine (.aspx) as a defa

Entity Framework 6 Code First 系列:无需修改实体和配置-在MySql中使用和SqlServer一致的并发控制

无需修改实体和配置,在MySql中使用和SqlServer一致的并发控制.修改RowVersion类型不可取,修改为Timestamp更不可行.Sql Server的RowVersion生成一串唯一的二进制保证Row的版本,无关TimeStamp,更无论TimeStamp的精度问题.使用MySql触发器只能解决uuid的插入的默认值和更新的随机值,由于MySql的自身为了防止无限递归的策略,它的触发器无法在当前表的触发器中更新当前表,所以触发器无法实现更新在SqlServer中由数据库生成的Ro

Entity Framework 6 Code First 系列:使SQLite.CodeFirst支持DropCreateDatabaseIfModelChanges和RowVersion

没什么好说的,能支持DropCreateDatabaseIfModelChanges和RowVersion的Sqlite谁都想要.EntityFramework7正在添加对Sqlite的支持,虽然EF7不知道猴年马月才能完成正式版,更不知道MySql等第三方提供程序会在什么时候跟进支持,但是EF7中的确出现了Sqlite的相关代码.Sqlite支持EF6的CodeFirst,只是不支持从实体生成数据库,估计有很多人因为这个原因放弃了使用它.现在SQLite.CodeFirst的简单实现可以让我们

《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述 (转)

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>第二版,慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一

Entity Framework之Code First

EF(Entity Framework )是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping (对象关系映射(Object RelationalMapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术.)) 解决方案. Entity Framework利用了抽象化数据结构的方式,将每个数据库对象都转换成应用程序对象 (entity),而数据字段都转换为属性 (property),关系则转换为结合属性(association),让数据

《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍 (转)

Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件开发整个生命周期的多语言环境. Visual Studio以及它发布的工具和服务提供了:设计.开发.单元测试.调试.软件配置和管理.构建管理和持续集成等等.很少有开发人员因为还没有使用它而担心(注:作者应该是表达不用担心VS的能力),Visual Studio是一个完整的工具集.Visual Stu