多对多添加身份用户
many to many add Identity user
您好,我正在尝试在我的 Asp.Net MVC 项目的实体 Entreprise 中添加一个 ApplicationUser(从 IdentityUser 派生 class)。它们之间存在多对多关系,自从我数据库中的 table ApplicationUserEntreprise 更新以来,它似乎起作用了。但是当我尝试做类似 user.Entreprisew 或 entreprise.Users 的事情时,它总是 return me null
这是我的 EntrepriseRepo 的方法 addUser,我尝试在我的实体之间执行 link
public void AddUser(ApplicationUser user , Entreprise entreprise) {
Entreprise entrepriseFromDb = base.FirstOrDefaultAsync(u => u.Id == entreprise.Id).Result;
if (entrepriseFromDb.ApplicationUsers == null)
{
entrepriseFromDb.ApplicationUsers = new List<ApplicationUser>();
entrepriseFromDb.ApplicationUsers.Add(user);
}
else {
entrepriseFromDb.ApplicationUsers.Add(user);
}
if (user.Entreprises == null) {
user.Entreprises = new List<Entreprise>();
user.Entreprises.Add(entrepriseFromDb);
}
else
{
user.Entreprises.Add(entrepriseFromDb);
}
}
我的控制器
public async Task AddUser(int id, string UserId)
{
Entreprise entreprise = await _services.Configuration.GetEntreprise(id);
ApplicationUser user = await _userManager.FindByIdAsync(UserId);
_services.Configuration.AddUserToEntrepriseAsync(user, entreprise);
await Details(id);
}
我的服务
public void AddUserToEntrepriseAsync(ApplicationUser user, Entreprise entreprise) {
_uow.Entreprises.AddUser(user, entreprise);
_uow.Save();
}
这是我的 SQL 数据库
这是我的 classes
应用程序用户
public class ApplicationUser : IdentityUser
{
public ICollection<Entreprise> Entreprises { get; set; }
}
企业
public class Entreprise
{
public int Id { get; set; }
public string Nom { get; set; }
public string LogoURL { get; set; }
public List<Groupe> Groupes { get; set; }
public List<Periode> Periodes { get; set; }
[NotMapped]
public int GroupesCount { get; set; }
[NotMapped]
public int EquipementsCount { get; set; }
[NotMapped]
public int PeriodesCount { get; set; }
public ICollection<ApplicationUser> ApplicationUsers{ get; set; }
}
有什么方法可以通过 entreprise.User 获取我的用户列表,通过 user.Entreprises 获取我的企业列表?
您的代码有几个问题。
首先,不要将 FirstOrDefault()
用作检索实体的拐杖方法。如果您期望一个实体应该在那里,请使用 Single()
。如果您需要一个实体或可能没有实体,请使用 SingleOrDefault()
。如果您期望有多个实体或可能什么都没有,并且只关心第一个实体,请使用 FirstOrDefault()
,但也只能与 OrderBy*()
方法结合使用。
接下来,不要在异步调用中使用 .Result
。要么一直使用等待的异步方法,要么使用同步方法。
传递可能分离的实体可能会在应用程序中导致很多混乱,因为您可能会得到一个分离的实体,一个被当前 DbContext 跟踪的实体,或者一个被另一个 DbContext 跟踪的实体。所有 3 种情况都可能导致不同的异常。传递实体也是一种开销,无论如何从 DbContext 获取实体时都不需要。我建议使用 ViewModel/DTO 作为 ApplicationUser 信息,必要时您可以使用它来创建新用户,并且只传递企业 ID。我们也会对与该企业关联的任何用户感兴趣,因此我们应该使用 .Include()
:
预先加载这些用户
public void AddUser(ApplicationUserViewModel userVm , int entrepriseId)
{
Entreprise entreprise = base
.Include(e => e.ApplicationUsers)
.Single(e => e.Id == entrepriseId);
// ...
接下来,避免在代码中初始化集合引用,而是在您的实体中初始化属性。代码“没问题”,但在被跟踪实体中重新初始化集合可能会导致问题,这只需要分散在周围的额外条件逻辑以在添加之前检查空集合。
public class Enterprise
{
// ...
public virtual ICollection<ApplicationUser> ApplicationUsers { get; internal set; } = new List<ApplicationUser>();
}
这同样适用于初始化其企业集合的 ApplicationUser。
在添加用户之前,我们的代码应该断言用户尚未关联,因为这也可能导致异常。您还应该考虑您是关联现有用户记录,还是在此过程中创建新用户。理想情况下,这些操作应该是原子的,因此创建全新用户是将用户关联到企业的独立操作。您的代码的危险在于 DbContext 可能会跟踪
所以代替:
if (entrepriseFromDb.ApplicationUsers == null)
{
entrepriseFromDb.ApplicationUsers = new List<ApplicationUser>();
entrepriseFromDb.ApplicationUsers.Add(user);
}
else
{
entrepriseFromDb.ApplicationUsers.Add(user);
}
if (user.Entreprises == null) {
user.Entreprises = new List<Entreprise>();
user.Entreprises.Add(entrepriseFromDb);
}
else
{
user.Entreprises.Add(entrepriseFromDb);
}
...我们做的更像是:
if (userVm.Id != 0 && enterprise.ApplicationUsers.Any(u => u.Id == userVm.Id))
return; // user is already associated.
ApplicationUser user = userVm.Id == 0
? createUser(userVm)
: base.ApplicationUsers.Single(u => u.Id == userVm.Id);
enterprise.ApplicationUsers.Add(user);
base.SaveChanges(); // Or saved further up the call stack/unit of work...
当我们关联通过导航属性配置关系的实体时,EF 将自动管理双向关系。我们希望避免做任何事情,比如检查和假设我们需要初始化集合,因为如果我们在一个错误的假设上搞砸了,这很容易导致错误。上面的代码假设我们可能会得到一个新用户或者应该是现有用户。如果我们得到一个现有用户,我们会检查企业是否已经拥有它,如果有就退出。否则我们准备添加它。如果 Id 不存在,我们使用视图模型中的详细信息创建一个新的 ApplicationUser。这可以使用 Automapper 和 .Map<ApplicationUser>()
来完成。否则我们从 DbContext 获取用户引用。我们在这里再次使用 Single()
作为完整性检查,以确保提供的用户详细信息有效。如果调用者传递的 UserId 为“101”并且没有包含该 Id 的记录,则应用程序应将此视为异常,因为数据状态确实存在问题或正在被篡改。
这将通过更多原子方法进一步简化,其中 CreateUser 操作与 AddUserToEnterprise 操作分开,后者可能始终获得现有的 User 引用。这样 AddUserToEnterprise 只需要传递一个 EnterpriseId 和一个 UserId,而不是有条件的逻辑来处理新用户和现有用户。
您好,我正在尝试在我的 Asp.Net MVC 项目的实体 Entreprise 中添加一个 ApplicationUser(从 IdentityUser 派生 class)。它们之间存在多对多关系,自从我数据库中的 table ApplicationUserEntreprise 更新以来,它似乎起作用了。但是当我尝试做类似 user.Entreprisew 或 entreprise.Users 的事情时,它总是 return me null
这是我的 EntrepriseRepo 的方法 addUser,我尝试在我的实体之间执行 link
public void AddUser(ApplicationUser user , Entreprise entreprise) {
Entreprise entrepriseFromDb = base.FirstOrDefaultAsync(u => u.Id == entreprise.Id).Result;
if (entrepriseFromDb.ApplicationUsers == null)
{
entrepriseFromDb.ApplicationUsers = new List<ApplicationUser>();
entrepriseFromDb.ApplicationUsers.Add(user);
}
else {
entrepriseFromDb.ApplicationUsers.Add(user);
}
if (user.Entreprises == null) {
user.Entreprises = new List<Entreprise>();
user.Entreprises.Add(entrepriseFromDb);
}
else
{
user.Entreprises.Add(entrepriseFromDb);
}
}
我的控制器
public async Task AddUser(int id, string UserId)
{
Entreprise entreprise = await _services.Configuration.GetEntreprise(id);
ApplicationUser user = await _userManager.FindByIdAsync(UserId);
_services.Configuration.AddUserToEntrepriseAsync(user, entreprise);
await Details(id);
}
我的服务
public void AddUserToEntrepriseAsync(ApplicationUser user, Entreprise entreprise) {
_uow.Entreprises.AddUser(user, entreprise);
_uow.Save();
}
这是我的 SQL 数据库
这是我的 classes
应用程序用户
public class ApplicationUser : IdentityUser
{
public ICollection<Entreprise> Entreprises { get; set; }
}
企业
public class Entreprise
{
public int Id { get; set; }
public string Nom { get; set; }
public string LogoURL { get; set; }
public List<Groupe> Groupes { get; set; }
public List<Periode> Periodes { get; set; }
[NotMapped]
public int GroupesCount { get; set; }
[NotMapped]
public int EquipementsCount { get; set; }
[NotMapped]
public int PeriodesCount { get; set; }
public ICollection<ApplicationUser> ApplicationUsers{ get; set; }
}
有什么方法可以通过 entreprise.User 获取我的用户列表,通过 user.Entreprises 获取我的企业列表?
您的代码有几个问题。
首先,不要将 FirstOrDefault()
用作检索实体的拐杖方法。如果您期望一个实体应该在那里,请使用 Single()
。如果您需要一个实体或可能没有实体,请使用 SingleOrDefault()
。如果您期望有多个实体或可能什么都没有,并且只关心第一个实体,请使用 FirstOrDefault()
,但也只能与 OrderBy*()
方法结合使用。
接下来,不要在异步调用中使用 .Result
。要么一直使用等待的异步方法,要么使用同步方法。
传递可能分离的实体可能会在应用程序中导致很多混乱,因为您可能会得到一个分离的实体,一个被当前 DbContext 跟踪的实体,或者一个被另一个 DbContext 跟踪的实体。所有 3 种情况都可能导致不同的异常。传递实体也是一种开销,无论如何从 DbContext 获取实体时都不需要。我建议使用 ViewModel/DTO 作为 ApplicationUser 信息,必要时您可以使用它来创建新用户,并且只传递企业 ID。我们也会对与该企业关联的任何用户感兴趣,因此我们应该使用 .Include()
:
public void AddUser(ApplicationUserViewModel userVm , int entrepriseId)
{
Entreprise entreprise = base
.Include(e => e.ApplicationUsers)
.Single(e => e.Id == entrepriseId);
// ...
接下来,避免在代码中初始化集合引用,而是在您的实体中初始化属性。代码“没问题”,但在被跟踪实体中重新初始化集合可能会导致问题,这只需要分散在周围的额外条件逻辑以在添加之前检查空集合。
public class Enterprise
{
// ...
public virtual ICollection<ApplicationUser> ApplicationUsers { get; internal set; } = new List<ApplicationUser>();
}
这同样适用于初始化其企业集合的 ApplicationUser。
在添加用户之前,我们的代码应该断言用户尚未关联,因为这也可能导致异常。您还应该考虑您是关联现有用户记录,还是在此过程中创建新用户。理想情况下,这些操作应该是原子的,因此创建全新用户是将用户关联到企业的独立操作。您的代码的危险在于 DbContext 可能会跟踪
所以代替:
if (entrepriseFromDb.ApplicationUsers == null)
{
entrepriseFromDb.ApplicationUsers = new List<ApplicationUser>();
entrepriseFromDb.ApplicationUsers.Add(user);
}
else
{
entrepriseFromDb.ApplicationUsers.Add(user);
}
if (user.Entreprises == null) {
user.Entreprises = new List<Entreprise>();
user.Entreprises.Add(entrepriseFromDb);
}
else
{
user.Entreprises.Add(entrepriseFromDb);
}
...我们做的更像是:
if (userVm.Id != 0 && enterprise.ApplicationUsers.Any(u => u.Id == userVm.Id))
return; // user is already associated.
ApplicationUser user = userVm.Id == 0
? createUser(userVm)
: base.ApplicationUsers.Single(u => u.Id == userVm.Id);
enterprise.ApplicationUsers.Add(user);
base.SaveChanges(); // Or saved further up the call stack/unit of work...
当我们关联通过导航属性配置关系的实体时,EF 将自动管理双向关系。我们希望避免做任何事情,比如检查和假设我们需要初始化集合,因为如果我们在一个错误的假设上搞砸了,这很容易导致错误。上面的代码假设我们可能会得到一个新用户或者应该是现有用户。如果我们得到一个现有用户,我们会检查企业是否已经拥有它,如果有就退出。否则我们准备添加它。如果 Id 不存在,我们使用视图模型中的详细信息创建一个新的 ApplicationUser。这可以使用 Automapper 和 .Map<ApplicationUser>()
来完成。否则我们从 DbContext 获取用户引用。我们在这里再次使用 Single()
作为完整性检查,以确保提供的用户详细信息有效。如果调用者传递的 UserId 为“101”并且没有包含该 Id 的记录,则应用程序应将此视为异常,因为数据状态确实存在问题或正在被篡改。
这将通过更多原子方法进一步简化,其中 CreateUser 操作与 AddUserToEnterprise 操作分开,后者可能始终获得现有的 User 引用。这样 AddUserToEnterprise 只需要传递一个 EnterpriseId 和一个 UserId,而不是有条件的逻辑来处理新用户和现有用户。