当DB中没有关系时,EF代码首先是多对多的关系

时间:2017-11-15 06:50:40

标签: c# entity-framework entity-framework-6 ef-code-first

假设我有两个类模型,如:

 public class AuthorityUser
{
    public string GUID { get; set; } 
    public int UserID { get; set; } 
    public ICollection<Authority1> Authorities { get; set; }
    public AuthorityUser()
    {
        Authorities = new HashSet<Authority1>();
    } 
}
public partial class Authority1
{
    public virtual int AID
    {
        get;
        set;
    }   
    public virtual ICollection<AuthorityUser> AuthorityUsers { get; set; }
    public Authority1()
    { 
        AuthorityUsers = new HashSet<AuthorityUser>();
    }
}

我将基于DB中的UserAuthorityMap连接表在它们之间建立多对多关系。

所以我这样做是为了在OnModelCreating()

中建立M:N关系
modelBuilder.Entity<AuthorityUser>().ToTable("Gainer").HasKey(x => x.UserID);
modelBuilder.Entity<Authority1>().ToTable("Authority").HasKey(x => x.AID);  
modelBuilder.Entity<AuthorityUser>()
          .HasMany<Authority1>(s => s.Authorities)
          .WithMany(c => c.AuthorityUsers)
          .Map(cs =>
          {
              cs.MapLeftKey("UserID");
              cs.MapRightKey("AID");
              cs.ToTable("UserAuthorityMap");
          });

正如我在标题中提到的,它们之间在DB中没有任何关系,因此DB中的图表如下图所示: enter image description here

当我运行时:

dbContext.AuthorityUsers.SingleOrDefault(x => x.UserID == 65);

不会从DB加载相关的Authorities

我应该使用HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)使其正确还是其他?

2 个答案:

答案 0 :(得分:1)

由于Authorities导航属性不是virtual,因此延迟加载已关闭,因此您还有2个选项可以加载它们。

选项1:急切加载

dbContext.AuthorityUsers.Include(x => x.Authorities).SingleOrDefault(x => x.UserID == 65);

注意IncludeSystem.Data.Entity命名空间中的扩展方法,因此请确保使用该命名空间。

选项2:明确加载

var users = dbContext.AuthorityUsers.SingleOrDefault(x => x.UserID == 65);
dbContext.Entry(users).Collection(p => p.Authorities).Load(); 

有关详细信息,请参阅this article

答案 1 :(得分:1)

如果您按照Entity Framework Code-First conventions进行操作,则不会出现此问题。

如果您确实需要为表格和主键使用非常规名称,那么AuthorityUserAuthority的两个ModelBuilder语句确实会执行您想要的操作。

但是,为了让您的多对多关系更轻松,请重新考虑您的方法,并按照entity-framework conventions for many-to-many relation

让您的生活更轻松

在您的情况下,这将导致两个变化:

  • 将AuthorityUser.Authorities设为虚拟
  • 让你的类代表你的表:让它变成简单的POCO:没有HashSet,没有构造函数。

使您的表类简单POCO的原因是因为该类表示数据库中的表。这个表没有HashSet,如果你不需要它,为什么要限制自己使用HashSet? (见后文)

在您的情况下,正确的多对多而无需告诉模型构建器您配置多对多将是:

class AuthorityUser
{
    // Primary Key (reconsider: Id)
    public int UserID { get; set; } 
    // an AuthorityUser belongs to zero or more Authorities (many-to-many)
    public virtual ICollection<Authority> Authorities { get; set; }
    ... // other properties
}
class Authority
{
    // primary key (reconsider: Id)
    public int AID {get; set;}
    // an Authority has zero or more AuthorityUsers (many-to-many)  
    public virtual ICollection<AuthorityUser> AuthorityUsers { get; set; }
    ... // other users
}

class MyDbContext : DbContext
{
    public DbSet<AuthorityUser> AuthorityUsers {get; set;}
    public DbSet<Authority> Authorities {get; set;}
}

您已经了解到需要一些模型构建来通知实体框架您的非传统主键和表名。

但是删除HashSet并在多对多中声明两个ICollection足以让实体框架理解多对多的意图。你不需要为此做一些模型构建。 Enityt Framework将创建一个联结表,并在需要时使用它。

使用多对多时,您不会使用联结表进行联接。相反,你会在集合中思考:

向我提供所有拥有xxx且其权限为yyy的权限用户

var result = dbContext.AuthorityUsers
    .Where(authorityUser => xxx)
    .Select(authorityUser => new
    {
        // take only the properties from authorityuser you'll need:
        UserId = authorityUser.UserId,
        GUID = authorityUser.GUID,

        // take all authorities from this authorityUser that have yyy
        Authorities = authorityUser.Authorities
            .Where(authority => yyy)
            .Select(authority => new
            {
                // take only the authority properties you'll use:
                AID = authority.AID,
                ...
            })
            .ToList(),
    });
}

实体框架知道这需要与联结表进行两次连接,并为您执行正确的SQL语句。

查询:给我所有权威机构......与他们所有的权限用户相似......

您的哈希集是否需要?

不,在您的所有查询中,实体框架将通过其自己的虚拟ICollection&lt; ...&gt;替换HashSet。

只有在您使用AuthorityUsers添加新权限时,您的HashSet才有用。没有HashSet,这就像:

Authority addedAuthority = myDbContext.Authorieties.Add(new Authority()
    {
         GUID = ...
         ... // other properties

         // this Authority has the following AuthorityUsers:
         AuthorityUsers = new List<AuthorityUsers>()
         {
              new AuthorityUser() {...},
              new AuthorityUser() {...},
              ...
         },
    });

而不是列表,你可以分配任何ICollection,如数组,甚至是字典:

Dictionary<int, AuthorityUser> authorityUsers = ...
Authority addedAuthority = myDbContext.Authorieties.Add(new Authority()
    {
         ...
         // this Authority has the following AuthorityUsers:
         AuthorityUsers = authorityUsers.Values,
    });

所以你看到删除HashSet可以让你更自由地提供ICollection:更好的可重用性。更少的代码,这使得当其他人需要维护它时更容易理解。此外,创建一个大部分时间没有使用的HashSet会浪费处理能力。