[英]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. 注意 :
Include
是System.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. 如果您确实需要为表和主键使用非常规名称,那么实际上您对
AuthorityUser
和Authority
两个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: 在您的情况下,这将导致两个更改:
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.