【问题标题】:多对多添加身份用户
【发布时间】:2022-01-21 15:33:19
【问题描述】:

您好,我正在尝试使用我的 Asp.Net MVC 项目在我的实体 Entreprise 中添加一个 ApplicationUser(来自 IdentityUser 的派生类)。它们之间有多对多的关系,而且自从我的数据库中的表 ApplicationUserEntreprise 得到更新后,它似乎可以工作。但是当我尝试做类似 user.Entreprisew 或 entreprise.Users 的事情时,它总是返回 null

这是我的 EntrepriseRepo 的 addUser 方法,我尝试在其中建立实体之间的链接

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 数据库

这是我的课程

应用用户

 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 获取我的企业列表?

【问题讨论】:

  • 刚刚意识到我可以使用参数 includeProperties 在我的 addUser 方法中访问 entreprise.Users。像这样:base.FirstOrDefaultAsync(u => u.Id == entreprise.Id, includeProperties: "ApplicationUsers").Result;。但我仍然想知道如何使用 user.Entreprises 访问我的用户的企业列表

标签: c# entity-framework asp.net-identity many-to-many repository-pattern


【解决方案1】:

您的代码存在一些问题。

首先,不要使用FirstOrDefault() 作为获取实体的拐杖方法。如果您希望有一个实体,请使用Single()。如果您希望有一个实体或可能没有实体,请使用SingleOrDefault()。如果您期望多个实体或可能什么都没有,并且只关心第一个,请使用FirstOrDefault(),但也只能与OrderBy*() 方法结合使用。

接下来,不要在异步调用中使用.Result。要么一直使用等待的异步方法,要么使用同步方法。

在应用程序中传递可能分离的实体可能会导致很多混乱,因为您可能会得到一个分离的实体,一个由当前 DbContext 跟踪的实体,或者一个由另一个 DbContext 跟踪的实体。所有 3 种情况都可能导致不同的异常。无论如何,当您从 DbContext 获取实体时,传递实体也是不需要的开销。我建议使用 ViewModel/DTO 作为 ApplicationUser 信息,如有必要,您可能会使用它来创建新用户,并且只需传递 Enterprise 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&lt;ApplicationUser&gt;() 来完成。否则,我们从 DbContext 中获取用户引用。我们在这里再次使用Single() 作为健全性检查,以确保提供的用户详细信息有效。如果调用者传递了“101”的 UserId 并且没有具有该 Id 的记录,则应用程序应将此视为异常,因为数据状态确实有问题或被篡改。

这将通过更多原子方法进一步简化,其中 CreateUser 操作与 AddUserToEnterprise 操作分开,后者可以期望始终获得现有的用户引用。这样,AddUserToEnterprise 只需要传递 EnterpriseId 和 UserId,而不是使用条件逻辑来处理新用户和现有用户。

【讨论】:

  • 谢谢你的帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
  • 2016-11-06
  • 1970-01-01
相关资源
最近更新 更多