c# - .NET Core 3.1 ChangePasswordAsync 内部异常 "Cannot update Identity column"

标签 c# entity-framework-core asp.net-core-identity .net-core-3.1

我正在将 .NET Core Web API 从 2.2 升级到 3.1。测试时ChangePasswordAsync函数,我收到以下错误消息:

Cannot update identity column '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 中包含的所有字段。 .

问题:

如何禁止在 ChangePasswordAsnyc 的 UPDATE 语句中填充标识列正在生成?我需要在模型中添加一个属性吗?我是否需要删除 UserId来自 objUser在将其传递到 ChangePasswordAsync 之前?或者是其他东西?

悬赏问题
我创建了一个自定义用户类,它扩展了 IdentityUser类(class)。在这个自定义类中,还有一个额外的 IDENTITY 列。在将 .NET Core 2.2 升级到 3.1 时,ChangePasswordAsync函数不再起作用,因为 3.1 方法正在尝试更新此 IDENTITY 列,而这在 2.1 中不会发生。

除了升级安装相关软件包之外,没有任何代码更改。接受的答案需要解决问题。

更新

不使用迁移,因为这会导致数据库和 Web API 及其关联模型之间的强制结合。在我看来,这违反了数据库、API 和 UI 之间的职责分离。但是,微软又回到了老路。

我有自己定义的 ADD、UPDATE 和 DELETE 方法,它们使用 EF 并设置 EntityState。但是,当单步执行 ChangePasswordAsync 的代码时,我没有看到任何这些函数被调用。就好像ChangePasswordAsync使用 EF 中的基本方法。所以,我不知道如何修改这种行为。来自伊万的回答。

注意:我确实发布了一个问题来尝试了解 ChangePasswordAsync方法在这里调用 EF [有人可以解释一下 ChangePasswordAsnyc 方法是如何工作的吗?][1]。

命名空间 ABC.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.
    }
}

命名空间 ABC.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.
    }
}

实体仓库
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;
        }   
    }
}

最佳答案

不确定这究竟是何时发生的,但在最新的 EF Core 2 (2.2.6) 中存在相同的行为,并且与以下 SO 问题 Auto increment non key value entity framework core 2.0 中的问题完全相同。 .因此,我可以在那里添加到我的答案中:

The problem here is that for identity columns (i.e. ValueGeneratedOnAdd) which are not part of a key, the AfterSaveBehavior is Save, which in turn makes Update method to mark them as modified, which in turn generates wrong UPDATE command.

To fix that, you have to set the AfterSaveBehavior like this (inside OnModelCreating override):

...


3.x 中唯一的区别是现在而不是 AfterSaveBehavior您拥有的房产GetAfterSaveBehaviorSetAfterSaveBehavior方法。要让 EF Core 始终从更新中排除该属性,请使用 SetAfterSaveBehaviorPropertySaveBehavior.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); // <--
    });

}

关于c# - .NET Core 3.1 ChangePasswordAsync 内部异常 "Cannot update Identity column",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60410556/

相关文章:

c# - LINQ to SQL - 在不创建新 DataContext 的情况下保存实体?

c# - 在 C# 中分配 "unmanaged"内存

C# 异步套接字服务器未收到来自 Java 客户端的响应

c# - 在 C# 中使用 MIDI 从 NeuroSky 耳机生成音乐

c# - Entity Framework 核心更新-数据库特定迁移

javascript - JS 获取 cookie ASP.NET Core Identity

entity-framework - 调用新的 DbContext 时,DbContextOptions 中的内容是什么?

c# - 无法识别 Visual Studio Code Entity Framework 核心添加迁移

c# - .NET Core 3.0 身份问题

asp.net-core - 是否可以公开来自 Asp.Net Core Identity 的安全标记作为 OIDC 的声明