我正在将 .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, theAfterSaveBehavior
isSave
, which in turn makesUpdate
method to mark them as modified, which in turn generates wrongUPDATE
command.To fix that, you have to set the
AfterSaveBehavior
like this (insideOnModelCreating
override):...
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); // <--
});
}
关于c# - .NET Core 3.1 ChangePasswordAsync 内部异常 "Cannot update Identity column",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60410556/