Не удается обновить пользователя - PullRequest
0 голосов
/ 16 октября 2018

Я использую AspNetCore.Identity для управления пользователем, зарегистрированным в моем приложении.У меня есть две таблицы:

  • AspNetUsers: создается по умолчанию
  • UserDetails: новая таблица, которая содержит дополнительную информацию для каждого зарегистрированного пользователя

the UserDetails таблица содержит FK, который указывает на UserId из AspNetUsers.Модели следующие:

public class User : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string LockoutMessage { get; set; }
    public string SessionId { get; set; }

    public virtual UserDetails UserDetail { get; set; }
}

public class UserDetails
{
    public string UserId { get; set; }
    public string Biography { get; set; }
    public string Country { get; set; }
    public string FacebookLink { get; set; }
    public string TwitterLink { get; set; }
    public string SkypeLink { get; set; }
    public string Address { get; set; }
    public string State { get; set; }
    public string Gender { get; set; }

    public User User { get; set; }
}

для обновления базы данных с этим новым полем я сделал это:

add-migration MigrationName -context AppContext
update-database

и это работает.Сейчас я пытаюсь обновить текущего пользователя, вошедшего в систему, поэтому я создал метод внутри класса UserRepository, который содержит следующее:

public UserProfileViewModel UpdateUserAsync(UserProfileViewModel updatedUser)
{
    var originalUser = _db.Users.FirstOrDefault(c => c.UserName == updatedUser.Username);

    originalUser.FirstName = updatedUser.FirstName;
    originalUser.UserDetail = new UserDetails();
    originalUser.UserDetail.Biography = updatedUser.Biography;

    _db.SaveChanges();
    return updatedUser;
}

По сути, я отправляю этому методу ViewModel изформа, которая содержит свойство для обновления, а затем я ищу пользователя в экземпляре контекста приложения _db и обновил все поля.

Проблема возникает, когда код достигает _db.SaveChanges(), фактически я получаю эту ошибку:

SqlException: Нарушение ограничения PRIMARY KEY 'PK_UserDetails'.Невозможно вставить дубликат ключа в объект 'dbo.UserDetails'.

Я полагаю, код пытается вставить поле вместо обновления, и это странно, кто-то может мне помочь?

ОБНОВЛЕНИЕ

Как вы можете видеть выше, я добавил дополнительное поле в таблицу AspNetUsers, для этого я наследую IdentityUser от User класса, который я использовал для определения сущности здесь:

services.AddIdentity<User, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>()
        .AddDefaultTokenProviders();

странно то, что когда я добавляю новую запись, таблица UserDetails заполняется правильно, но когда мне нужно обновить, я предполагаю UserDetail как ноль, и в то же время я не могу добавить другую UserDetail, потому что она уже существуетсгенерировал CONSTRAINT исключение.

1 Ответ

0 голосов
/ 16 октября 2018

Entity Framework Core не загружает связанные данные, если вы не указали это сделать, используя один из нескольких параметров, все из которых описаны в Загрузка связанных данных .При выполнении следующего запроса к DbSet:

_db.Users
    .FirstOrDefault(c => c.UserName == updatedUser.Username);

значение свойства навигации UserDetail не заполняется.Чтобы обойти это, вы можете использовать следующую модификацию:

_db.Users
    .Include(u => u.UserDetail)
    .FirstOrDefault(c => c.UserName == updatedUser.Username);

Как вы указали в комментариях к самому OP, это приводит к новой ошибке, в которой этот подход сочетается с * 1011.* жалуется, что User уже отслеживается.Также в комментариях вы задали следующий вопрос:

На данный момент я исправил: await _db.SaveChangesAsync();поэтому я думаю, что мне нужно использовать экземпляр контекста базы данных вместо usermanager, к сожалению, некоторые методы, такие как хеширование пароля, доступны только в менеджере пользователей, невозможно использовать только экземпляр db?

ВЧтобы достичь желаемого результата, вы должны использовать что-то вроде этого:

public async Task<UserProfileViewModel> UpdateUserAsync(UserProfileViewModel updatedUser)
{
    var originalUser = await _userManager.FindByNameAsync(updatedUser.Username);

    // This is the "magic" (ok, it's a bit of a hack).
    _db.Entry(originalUser).Reference(u => u.UserDetail).Load();

    ...

    await _userManager.UpdateUserAsync(originalUser);
    return updatedUser;
}

Этот конкретный подход был предложен в выпуске GitHub здесь и, по сути, является просто еще одним способомзагрузить связанные данные (как описано в приведенной мной ссылке).

Примечание. Я преобразовал вашу функцию UpdateUserAsync в async/await, чтобы использовать функции UserManager.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...