简体   繁体   English

当数据库中没有关系时,EF代码首先进行多对多关系

[英]EF code first many to many relation when no relation in DB

Suppose I have two classes model like: 假设我有两个类似的模型:

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

I am going to make Many To Many relation between them based on UserAuthorityMap connected table in DB. 我将基于数据库中的UserAuthorityMap连接表在它们之间建立多对多关系。

so I did this to make M:N relation in OnModelCreating() 所以我这样做是为了在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");
          });

As I mentioned in title there is no relation between them in DB so the diagram in DB is like picture below : 正如我在标题中提到的,DB中它们之间没有关系,因此DB中的图如下图所示: 在此处输入图片说明

when I run this : 当我运行这个:

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

the related Authorities won't be loaded from DB. 相关Authorities将不会从数据库中加载。

so should I use HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) to make it right or something else? 所以我应该使用HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)使其正确吗?

Since Authorities navigation property is not virtual , lazy loading has been turned off and thus you have 2 options left to load them. 由于Authorities导航属性不是virtual ,因此已关闭了延迟加载,因此还有2个选项可以加载它们。

Option 1: Eager Loading 选项1:渴望加载

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

Note : Include is an extension method in the System.Data.Entity namespace so make sure you are using that namespace. 注意IncludeSystem.Data.Entity命名空间中的扩展方法,因此请确保您正在使用该命名空间。

Option 2: Explicit Loading 选项2:显式加载

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

Please see this article for more details. 请参阅本文以获取更多详细信息。

If you followed the Entity Framework Code-First conventions you wouldn't have this problem. 如果遵循Entity Framework Code-First约定 ,则不会有此问题。

If you really need to use non-conventional names for your tables and your primary keys, then indeed your two ModelBuilder statements for AuthorityUser and Authority will do what you want. 如果您确实需要为表和主键使用非常规名称,那么实际上您对AuthorityUserAuthority两个ModelBuilder语句就可以满足您的要求。

However, to make your many-to-many relationship easier, reconsider your method, and make your life easier by following the entity-framework conventions for many-to-many relation 但是,要使多对多关系更容易,请重新考虑方法,并遵循多对多关系实体框架约定使生活更轻松

In your case this would lead to two changes: 在您的情况下,这将导致两个更改:

  • Make AuthorityUser.Authorities virtual 将AuthorityUser.Authorities设为虚拟
  • Let your classes represent your tables: let it be simple POCOs: no HashSet, no Constructor. 让您的类代表您的表:让它成为简单的POCO:没有HashSet,没有构造函数。

The reason to make your table classes simple POCOs, is because the class represents a table in a database. 之所以将表类简化为POCO,是因为该类表示数据库中的表。 This table has no HashSet, and if you don't need it, why limit yourself to a HashSet? 该表没有HashSet,如果不需要它,为什么将自己限制为HashSet? (See later) (请参阅稍后)

In your case the proper many-to-many without the need tell the model builder that you configured a many-to-many would be: 在您的情况下,无需告诉模型构建器您配置了多对多的适当的多对多将是:

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

You already understood that you need some Model Building to inform entity framework about your non-conventional primary keys and table names. 您已经了解到,您需要一些模型构建来告知实体框架您的非常规主键和表名。

But removing the HashSet and declaring both ICollections in the many-to-many is enough for entity framework to understand that a many-to-many is intended. 但是,删除HashSet并在多对多声明中同时声明两个ICollections足以使实体框架了解多对多的意图。 You don't need to do some model building for this. 您无需为此进行一些模型构建。 Enityt Framework will create a junction table and use it whenever needed. Enityt Framework将创建一个联结表,并在需要时使用它。

When using the many-to-many you won't do a join with the junction table. 当使用多对多时,您将不会与联结表进行联接。 Instead you think in collections: 相反,您在集合中认为:

Give me all AuthorityUsers that have xxx with their Authorities that have yyy 给我所有具有xxx和yyy的AuthorityUser

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(),
    });
}

Entity Framework knows that this needs two joins with the junction table, and perform the proper SQL statement for you. 实体框架知道这需要与联结表进行两次联接,并为您执行适当的SQL语句。

The query: give me all Authorities that ... with all their AuthorityUsers which ... is similar. 查询:给我所有具有类似权限的Authority及其所有AuthorityUsers。

Is your hashset needed? 需要您的哈希集吗?

No, in all your queries, entity framework will replace the HashSet by its own virtual ICollection<...>. 不,在所有查询中,实体框架都将用其自己的虚拟ICollection <...>代替HashSet。

Your HashSet would only be useful if you'd add a new Authority with its AuthorityUsers. 仅当您要添加带有AuthorityUsers的新Authority时,您的HashSet才有用。 Without HashSet this would be like: 如果没有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() {...},
              ...
         },
    });

Instead of a List you couls assign any ICollection, like an array, or even from a Dictionary: 您可以代替列表来为列表分配任何ICollection,例如数组,甚至来自Dictionary:

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

So you see that removing the HashSet give you more freedom to provide the ICollection: Better reusability. 因此,您会看到,删除HashSet给您提供ICollection的更多自由:更好的可重用性。 Less code, which makes it better understandable when someone else needs to maintain it. 更少的代码,这使得在需要其他人维护时更好理解。 Besides it is a waste of processing power to create a HashSet that is most of the time not used. 此外,创建大多数时间不使用的HashSet会浪费处理能力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM