多对多添加身份用户

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,而不是有条件的逻辑来处理新用户和现有用户。