首页 > 解决方案 > AspNetCore.Identity UserManager CreateAsync 无法保存关系

问题描述

我有一个这样的模型:

public class UserIdentity : IdentityUser
{
    public User User { get; set; }
}

public class User : Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Occupation Occupation { get; set; }
    public Country Country { get; set; }
    public City City { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public GenderEnum Gender { get; set; }
}

IdentityUser 在AspNetCore.Identity命名空间中。此外,我将Urf.Core用于我的存储库和服务层,而 Entity 用于在该库中指定可跟踪的实体。

在我的应用程序层中,我有一个用于注册用户的服务,我用它UserManager<UserIdentity>来保存用户。

public async Task Register(UserRegistrationDto dto)
    {
        var newUser = new User();

        var country = GetCountry(dto.CountryName);
        var city = GetCity(country, dto.CityName, dto.CityId.Value);

        var ethnicity = GetEthnicity(dto.EthnicityId.Value);
        var occupation = GetOccupation(dto.OccupationId.Value);

        newUser.Country = country;
        newUser.City = city;
        newUser.Occupation = occupation;
        newUser.Ethnicity = ethnicity;

        newUser.DateOfBirth = dto.DateOfBirth;

        newUser.Name = dto.Name;
        newUser.Surname = dto.Surname;
        newUser.Gender = (GenderEnum)dto.Gender.Value;

        _userService.Insert(newUser);

        var newIdentity = new UserIdentity {UserName = dto.Email, Email = dto.Email, User = newUser};
        var result = await _userManager.CreateAsync(newIdentity, dto.Password);

        //await _unitOfWork.SaveChangesAsync();
    }

代码说明:

GetCountryand ,GetCity两者都检查城市和国家是否存在,如果不存在,则创建它们。我将用户及其与 Urf 服务的所有关系插入其中_userService.Insert(newUser),并将其全部保存在单个事务中,再次使用 Urf 工作单元,await _unitOfWork.SaveChangesAsync()

问题:

放置SaveChangesAsync()在之前_userManager.CreateAsync(),创建用户,城市,国家和所有没有任何问题。但是,如果我调用_userManager.CreateAsync(), ef 会为国家、城市和用户表中的 id 列抛出异常“无法为标识列插入显式值...”。

我想UserIdentity在一次调用中保存和其余的关系SaveChanges(),但看起来_userManager.CreateAsync()不能像这样保存关系_unitOfWork.SaveChangesAsync()

临时解决方案:

为了暂时通过,我颠倒了 and 中的关系UserIdentityUser我首先创建身份,如果成功,我插入其余的:

    public async Task Register(UserRegistrationDto dto)
    {
        var newIdentity = UserIdentity.CreateIdentity(dto.Email, dto.Email, dto.Phone);
        var result = await _userManager.CreateAsync(newIdentity, dto.Password);
        if (result.Succeeded)
        {
            var newUser = new User();

            var country = GetCountry(dto.CountryName);
            var city = GetCity(country, dto.CityName, dto.CityId.Value);

            var ethnicity = GetEthnicity(dto.EthnicityId.Value);
            var occupation = GetOccupation(dto.OccupationId.Value);

            newUser.Identity = newIdentity;
            newUser.Country = country;
            newUser.City = city;
            newUser.Occupation = occupation;
            newUser.Ethnicity = ethnicity;

            newUser.DateOfBirth = dto.DateOfBirth;

            newUser.Name = dto.Name;
            newUser.Surname = dto.Surname;
            newUser.Gender = (GenderEnum)dto.Gender.Value;

            _userService.Insert(newUser);

            await _unitOfWork.SaveChangesAsync();
        }
    }

但如您所见,它将它们保存在两个单独的事务中,如果_unitOfWork.SaveChangesAsync();由于任何原因失败,则创建身份并且我无法回滚(可能但很脏)

有谁知道为什么我解释的问题会发生?

标签: c#asp.net-coreentity-framework-core

解决方案


您忽略了发布您的完整控制器,特别是正在注入的服务,但根据您的问题,我猜您正在注入UserManager<IdentityUser>.

这里的类型参数是实际将要检索、持久化等的内容。因此,如果您尝试保存UserIdentity实例,它将被向上转换为IdentityUser(没有您的User属性),就是将要创建的内容. 如果你需要使用UserIdentity,那么你需要注入UserManager<UserIdentity>

但是,您在这里的设计是完全错误的。通用用户类型的全部意义在于允许可扩展性。您应该创建一个类,该类直接从那里继承IdentityUser并为您的用户指定任何其他属性。您不需要也不应该通过组合添加单独的配置文件样式类。换句话说:

public class User : IdentityUser
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Occupation Occupation { get; set; }
    public Country Country { get; set; }
    public City City { get; set; }
    public Ethnicity Ethnicity { get; set; }
    public GenderEnum Gender { get; set; }
}

然后,在 Startup.cs 中:

services.AddDefaultIdentity<User>();

在您的控制器中,直接注入UserManager<User>和使用UserUserIdentity进入垃圾箱。


推荐阅读