AspNetCore.Identity UserManager CreateAsync无法保存关系

时间:2019-02-11 14:49:01

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

我有一个这样的模型:

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();
    }

代码说明:

GetCountryGetCity都检查城市和国家/地区是否存在,如果不存在,则会创建城市和国家/地区。我在_userService.Insert(newUser)中插入用户及其与Urf服务的所有关系,并将其与Urf工作单元await _unitOfWork.SaveChangesAsync()

一起保存在单个事务中

问题:

SaveChangesAsync()放在_userManager.CreateAsync()之前,可以毫无问题地创建用户,城市,国家和地区。但是,如果我调用_userManager.CreateAsync(),则ef会为国家,城市和用户表中的id列引发异常“无法为标识列插入显式值...”。

我想在一次UserIdentity调用中保存SaveChanges()和其余关系,但是看起来_userManager.CreateAsync()无法像_unitOfWork.SaveChangesAsync()那样保存关系。 / p>

临时解决方案:

现在要获得通行证,我颠倒了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();由于任何原因失败,则将创建身份并且我无法回滚(可能但很脏)

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

1 个答案:

答案 0 :(得分:0)

您忽略发布完整的控制器,特别是要注入的服务,但是基于您的问题,我猜您是在注入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>并直接与User一起使用。 UserIdentity丢进了垃圾桶。