I am working with WebAPI, EF Code First and have a problem concerning many-to-many relationships:
我正在使用WebAPI,EF Code First,并且遇到有关多对多关系的问题:
I am working with a custom user inherited from "IdentityUser" who can have many Projects.
我正在使用从“IdentityUser”继承的自定义用户,该用户可以拥有许多项目。
These Projects can now have multiple users. In Addition to this I want to store additional fields in a mapping table.
这些项目现在可以拥有多个用户。除此之外,我想在映射表中存储其他字段。
public class MyCustomUser : IdentityUser
{
// blah blah...
public virtual ICollection<Project> Projects { get; set; }
}
public class Project
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<MyCustomUser> Users { get; set; }
}
public class Users2Projects
{
public int UserId { get; set; }
public int ProjectId { get; set; }
public virtual MyCustomUser User { get; set; }
public virtual Project Project { get; set; }
public string AdditionalField1 {get;set;}
public string AdditionalField1 {get;set;}
}
The question is: How do I have to set the relations (FluentAPI) to insert a Project for a User which is already there? Because the user can use the application no matter if he has a Project or not.
问题是:我如何设置关系(FluentAPI)为已经存在的用户插入项目?因为用户可以使用该应用程序,无论他是否有项目。
So the User CAN have a Project, but a Project has to have at least one User. I am getting the User through the Owin "UserManager" "FindById" which returns "MyCustomUser". Creating a new Users2Project-Class, adding the new Project to it, adding the user to it fails because EF wants to store a new User. How can I solve this?
因此,用户可以有一个项目,但项目必须至少有一个用户。我通过Owin“UserManager”“FindById”获取用户,返回“MyCustomUser”。创建新的Users2Project-Class,向其添加新项目,将用户添加到其中失败,因为EF想要存储新用户。我怎么解决这个问题?
Kind Regards
2 个解决方案
#1
You don't need to set anything with FluentAPI when you explicitly declare your many-to-many relationship entity. But you should remove the following lines from User
and Project
, respectively:
当您明确声明多对多关系实体时,不需要使用FluentAPI设置任何内容。但是你应该分别从User和Project中删除以下行:
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection
public virtual ICollection<MyCustomUser> Users { get; set; }
public virtual ICollection
and add on both entities:
并添加两个实体:
public virtual ICollection<Users2Projects> Users2Projects { get; set; }
public virtual ICollection
To add a new Project
for an existing User
you can:
要为现有用户添加新项目,您可以:
var relation = new Users2Projects() {
User = existingUser, // reference retrieved from Context.
Project = new Project() { Title = "Project Alpha" }
}
Context.Users2Projects.Add(relation);
Context.SaveChanges();
As far as I know, it is not possible to force the Project
to have at least one User
using a explicit relationship class on Entity Framework, so you would have to check that programmatically upon inserting a Project
into the Context
:
据我所知,不可能强制项目至少有一个用户在Entity Framework上使用显式关系类,因此在将项目插入Context时必须以编程方式检查:
if (project.Users2Projects.Count() == 0)
throw new Exception("Project must have at least one user.")
Context.Projects.Add(project);
Context.SaveChanges();
and upon deleting an User
you do the check before:
删除用户后,您可以进行以下检查:
var projectsToDelete = new List<Project>();
foreach (var relationship in user.Users2Projects)
{
if (relationship.Project.Users2Projects.Count() <= 1)
projectsToDelete.Add(relationship.Project);
}
Context.MyCustomUsers.Remove(user);
Context.Projects.RemoveRange(projectsToDelete);
Context.SaveChanges();
#2
You are clearly missing a conceptual entity here although you actually already modeled it a bit as your Users2Projects
mapping table. You may want to untangle a few concepts around the bounded contexts that exist in your application, and look again at your requirements
虽然你实际上已经将它作为Users2Projects映射表进行了一些建模,但你显然在这里缺少一个概念实体。您可能想要围绕应用程序中存在的有界上下文解开几个概念,并再次查看您的要求
-
User administration (i.e. administration of users that are allowed to access your system) seems to be a different concern than project administration:
用户管理(即允许访问系统的用户的管理)似乎与项目管理不同:
Because the user can use the application no matter if he has a Project or not.
因为用户可以使用该应用程序,无论他是否有项目。
-
In project administration, for the assignment of a user to a project, it appears there is additional information specific to the project that needs to be captured as part of the
ProjectMemberAssignment
. YourProject
appears to be the aggregate root for this bounded context, and should be made responsible for managing it's member assignments:在项目管理中,为了将用户分配给项目,似乎有一些特定于项目的附加信息需要作为ProjectMemberAssignment的一部分被捕获。您的项目似乎是此有界上下文的聚合根,应该负责管理它的成员分配:
public class Project { public virtual int Id { get; private set; } public string Title { get; set; } public void AssignMember(int userId, string someField, string someOtherField) { if (MemberAssignments.Any(x => x.UserId == userId)) throw new InvalidOperationException("User already assigned"); MemberAssignments.Add(new ProjectMemberAssignment(Id, userId, someField, someOtherField)); } // Other behavior methods. public virtual ICollection<ProjectMemberAssignment> MemberAssignments { get; set; } }
Note that you can get the projects for a user using a simple query, without the need to maintain a many-to-many relationship in your model:
请注意,您可以使用简单查询为用户获取项目,而无需在模型中维护多对多关系:
var user = ...;
var projectsForUser =
from project in db.Projects
where project.MemberAssignments.Any(x => x.userId == user.UserId)
select project;
#1
You don't need to set anything with FluentAPI when you explicitly declare your many-to-many relationship entity. But you should remove the following lines from User
and Project
, respectively:
当您明确声明多对多关系实体时,不需要使用FluentAPI设置任何内容。但是你应该分别从User和Project中删除以下行:
public virtual ICollection<Project> Projects { get; set; }
public virtual ICollection
public virtual ICollection<MyCustomUser> Users { get; set; }
public virtual ICollection
and add on both entities:
并添加两个实体:
public virtual ICollection<Users2Projects> Users2Projects { get; set; }
public virtual ICollection
To add a new Project
for an existing User
you can:
要为现有用户添加新项目,您可以:
var relation = new Users2Projects() {
User = existingUser, // reference retrieved from Context.
Project = new Project() { Title = "Project Alpha" }
}
Context.Users2Projects.Add(relation);
Context.SaveChanges();
As far as I know, it is not possible to force the Project
to have at least one User
using a explicit relationship class on Entity Framework, so you would have to check that programmatically upon inserting a Project
into the Context
:
据我所知,不可能强制项目至少有一个用户在Entity Framework上使用显式关系类,因此在将项目插入Context时必须以编程方式检查:
if (project.Users2Projects.Count() == 0)
throw new Exception("Project must have at least one user.")
Context.Projects.Add(project);
Context.SaveChanges();
and upon deleting an User
you do the check before:
删除用户后,您可以进行以下检查:
var projectsToDelete = new List<Project>();
foreach (var relationship in user.Users2Projects)
{
if (relationship.Project.Users2Projects.Count() <= 1)
projectsToDelete.Add(relationship.Project);
}
Context.MyCustomUsers.Remove(user);
Context.Projects.RemoveRange(projectsToDelete);
Context.SaveChanges();
#2
You are clearly missing a conceptual entity here although you actually already modeled it a bit as your Users2Projects
mapping table. You may want to untangle a few concepts around the bounded contexts that exist in your application, and look again at your requirements
虽然你实际上已经将它作为Users2Projects映射表进行了一些建模,但你显然在这里缺少一个概念实体。您可能想要围绕应用程序中存在的有界上下文解开几个概念,并再次查看您的要求
-
User administration (i.e. administration of users that are allowed to access your system) seems to be a different concern than project administration:
用户管理(即允许访问系统的用户的管理)似乎与项目管理不同:
Because the user can use the application no matter if he has a Project or not.
因为用户可以使用该应用程序,无论他是否有项目。
-
In project administration, for the assignment of a user to a project, it appears there is additional information specific to the project that needs to be captured as part of the
ProjectMemberAssignment
. YourProject
appears to be the aggregate root for this bounded context, and should be made responsible for managing it's member assignments:在项目管理中,为了将用户分配给项目,似乎有一些特定于项目的附加信息需要作为ProjectMemberAssignment的一部分被捕获。您的项目似乎是此有界上下文的聚合根,应该负责管理它的成员分配:
public class Project { public virtual int Id { get; private set; } public string Title { get; set; } public void AssignMember(int userId, string someField, string someOtherField) { if (MemberAssignments.Any(x => x.UserId == userId)) throw new InvalidOperationException("User already assigned"); MemberAssignments.Add(new ProjectMemberAssignment(Id, userId, someField, someOtherField)); } // Other behavior methods. public virtual ICollection<ProjectMemberAssignment> MemberAssignments { get; set; } }
Note that you can get the projects for a user using a simple query, without the need to maintain a many-to-many relationship in your model:
请注意,您可以使用简单查询为用户获取项目,而无需在模型中维护多对多关系:
var user = ...;
var projectsForUser =
from project in db.Projects
where project.MemberAssignments.Any(x => x.userId == user.UserId)
select project;