. NET Core 3.1 ChangePasswordAsyn c Внутреннее исключение "Не удается обновить столбец идентификации" - PullRequest
5 голосов
/ 26 февраля 2020

Я обновляю. NET Core Web API с 2.2 до 3.1. При тестировании функции ChangePasswordAsync я получаю следующее сообщение об ошибке:

Невозможно обновить столбец идентификатора 'UserId'.

Я запустил профиль SQL и я Можно видеть, что столбец Identity не включен в оператор 2.2 UPDATE, но находится в 3.1.

Соответствующая строка кода возвращает NULL, в отличие от успеха или ошибок, и выглядит следующим образом:

objResult = await this.UserManager.ChangePasswordAsync(objUser, objChangePassword.OldPassword, objChangePassword.NewPassword);

Реализация ChangePasswordAsnyc выглядит следующим образом (код, усеченный для краткость).

Примечание: AspNetUsers расширяет IdentityUser.

[HttpPost("/[controller]/change-password")]
public async Task<IActionResult> ChangePasswordAsync([FromBody] ChangePassword objChangePassword)
{
    AspNetUsers objUser = null; 
    IdentityResult objResult = null;    

    // retrieve strUserId from the token.

    objUser = await this.UserManager.FindByIdAsync(strUserId);
    objResult = await this.UserManager.ChangePasswordAsync(objUser, objChangePassword.OldPassword, objChangePassword.NewPassword);
    if (!objResult.Succeeded)
    {
        // Handle error.
    }

    return this.Ok(new User(objUser));
}

UserId включается в objResult вместе со многими другими полями, которые затем возвращаются в конце метода. Из того, что я могу сказать, не имея возможности пошагово выполнять метод ChangePasswordAsync, функция обновляет все поля, содержащиеся в objUser.

Вопрос:

Как запретить заполнение столбца идентификации в инструкции UPDATE, которую генерирует ChangePasswordAsnyc? Нужно ли добавить атрибут в модель? Нужно ли удалять UserId из objUser перед тем, как передать его в ChangePasswordAsync? Или что-то еще?

Вопрос о награде Я создал пользовательский класс пользователя, который расширяет класс IdentityUser. В этом пользовательском классе есть дополнительный столбец IDENTITY. При обновлении. NET Core 2.2 до 3.1 функция ChangePasswordAsync больше не работает, поскольку метод 3.1 пытается обновить этот столбец IDENTITY, тогда как в 2.1 это не происходит.

Не было никакого изменения кода кроме обновления установки соответствующих пакетов. Принятый ответ должен устранить проблему.

ОБНОВЛЕНИЕ

Миграции не используются, поскольку это приводит к принудительному браку между базой данных и веб-API с соответствующими моделями. , На мой взгляд, это нарушило разделение обязанностей между базой данных, API и пользовательским интерфейсом. Но это опять Microsoft по-старому.

У меня есть свои собственные определенные методы ADD, UPDATE и DELETE, которые используют EF и устанавливают EntityState. Но при пошаговом выполнении кода для ChangePasswordAsync я не вижу ни одной из этих вызванных функций. Это как если бы ChangePasswordAsync использовал базовые методы в EF. Итак, я не знаю, как изменить это поведение. из ответа Ивана.

Примечание: я опубликовал вопрос, чтобы попытаться понять, как метод ChangePasswordAsync вызывает EF здесь [Может кто-нибудь объяснить, как работает метод ChangePasswordAsny c?] [1].

пространство имен AB C .Model.AspNetCore

using ABC.Common.Interfaces;
using ABC.Model.Clients;
using Microsoft.AspNetCore.Identity;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ABC.Model.AspNetCore
{
    // Class for AspNetUsers model
    public class AspNetUsers : IdentityUser
    {
        public AspNetUsers()
        {
            // Construct the AspNetUsers object to have some default values here.
        }

        public AspNetUsers(User objUser) : this()
        {
            // Populate the values of the AspNetUsers object with the values found in the objUser passed if it is not null.
            if (objUser != null)
            {
                this.UserId = objUser.UserId; // This is the problem field.
                this.Email = objUser.Email;
                this.Id = objUser.AspNetUsersId;
                // Other fields.
            }
        }

        // All of the properties added to the IdentityUser base class that are extra fields in the AspNetUsers table.
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int UserId { get; set; } 
        // Other fields.
    }
}

пространство имен AB C .Model.Clients

using ABC.Model.AspNetCore;
using JsonApiDotNetCore.Models;
using System;
using System.ComponentModel.DataAnnotations;

namespace ABC.Model.Clients
{
    public class User : Identifiable
    {
        public User()
        {
            // Construct the User object to have some default values show when creating a new object.
        }

        public User(AspNetUsers objUser) : this()
        {
            // Populate the values of the User object with the values found in the objUser passed if it is not null.
            if (objUser != null)
            {
                this.AspNetUsersId = objUser.Id;
                this.Id = objUser.UserId;    // Since the Identifiable is of type Identifiable<int> we use the UserIdas the Id value.
                this.Email = objUser.Email;
                // Other fields.
            }
        }

        // Properties
        [Attr("asp-net-users-id")]
        public string AspNetUsersId { get; set; }

        [Attr("user-id")]
        public int UserId { get; set; }

        [Attr("email")]
        public string Email { get; set; }

        [Attr("user-name")]
        public string UserName { get; set; }

        // Other fields.
    }
}

EntitiesRepo

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace ABC.Data.Infrastructure
{  
    public abstract class EntitiesRepositoryBase<T> where T : class
    {
        #region Member Variables
        protected Entities m_DbContext = null;
        protected DbSet<T> m_DbSet = null;
        #endregion


        public virtual void Update(T objEntity)
        {
            this.m_DbSet.Attach(objEntity);
            this.DbContext.Entry(objEntity).State = EntityState.Modified;
        }   
    }
}

Ответы [ 3 ]

2 голосов
/ 28 февраля 2020

Не совсем точно, когда это произошло, но такое же поведение существует в последнем EF Core 2 (2.2.6), а также является точно такой же проблемой, как и в следующем вопросе SO Автоматическое увеличение структуры сущности, не являющейся ключевым значением ядро 2.0 . Следовательно, я мало что могу добавить к своему ответу:

Проблема здесь в том, что для столбцов идентификации (т. Е. ValueGeneratedOnAdd), которые не являются частью ключа AfterSaveBehavior равен Save, что, в свою очередь, позволяет Update пометить их как измененные, что, в свою очередь, приводит к неправильной команде UPDATE.

Чтобы это исправить, необходимо установить AfterSaveBehavior вот так (внутри OnModelCreating переопределить):

...

Единственное отличие в 3.x состоит в том, что теперь вместо свойства AfterSaveBehavior у вас есть GetAfterSaveBehavior и SetAfterSaveBehavior методы. Чтобы EF Core всегда исключал свойство из обновлений, используйте SetAfterSaveBehavior с PropertySaveBehavior.Ignore, например

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

...

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<AspNetUsers>(builder =>
    {
        builder.Property(e => e.UserId).ValueGeneratedOnAdd()
            .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); // <--
    });

}
0 голосов
/ 28 февраля 2020

Создание и запуск миграций:

dotnet ef migrations add IdentityColumn
dotnet ef database update
0 голосов
/ 28 февраля 2020

Вы могли бы попытаться использовать свойство EntityState, чтобы пометить его как неизмененный?

db.Entry(model).State = EntityState.Modified;
db.Entry(model).Property(x => x.UserId).IsModified = false;
db.SaveChanges();

см. Исключить свойство при обновлении в Entity Framework и https://docs.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state

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