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

本文转自:http://www.cnblogs.com/easygame/p/3622893.htmlhtml

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);
    }
}
实体类配置

1、肯定依赖关系:ide

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

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

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

(1)B依赖于A,A能够对B的存在一无所知。code

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

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

推荐的写法:blog

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表的变化。

3、配置依赖关系

配置文件的基类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

4、配置关联类型

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

4、可选导航属性

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

对于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);
    }
}
导航属性

5、显式外键属性

对于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);
    }
}
外键配置

6、级联删除配置

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

7、关于双向依赖

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
相关文章
相关标签/搜索