J W*_*ezy 6 c# entity-framework-core asp.net-core-identity .net-core-3.1
我正在将 .NET Core Web API 从 2.2 升级到 3.1。测试该ChangePasswordAsync功能时,我收到以下错误消息:
无法更新标识列“UserId”。
我运行了一个 SQL Profile,我可以看到 Identity 列不包含在 2.2 UPDATE 语句中,但它包含在 3.1 中。
有问题的代码行返回 NULL,而不是成功或错误,如下所示:
objResult = await this.UserManager.ChangePasswordAsync(objUser, objChangePassword.OldPassword, objChangePassword.NewPassword);
Run Code Online (Sandbox Code Playgroud)
的实现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));
}
Run Code Online (Sandbox Code Playgroud)
在UserId被包括在objResult,有很多其他领域,然后将其在该方法结束时返回的沿。据我所知,在无法逐步执行该ChangePasswordAsync方法的情况下,该函数会更新objUser.
题:
如何禁止在ChangePasswordAsnyc生成的 UPDATE 语句中填充标识列?我需要在模型中添加一个属性吗?在将其传递到 之前,我是否需要将其UserId从 中删除?或者是其他东西?objUserChangePasswordAsync
赏金问题
我创建了一个扩展IdentityUser类的自定义用户类。在这个自定义类中,还有一个额外的 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.
}
}
Run Code Online (Sandbox Code Playgroud)
命名空间 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.
}
}
Run Code Online (Sandbox Code Playgroud)
实体仓库
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
不确定这种情况何时发生,但最新的 EF Core 2 (2.2.6) 中存在相同的行为,并且与以下 SO 问题Autoincrementnonkeyvalueentityframeworkcore2.0中的问题完全相同。因此,我无法在我的答案中添加太多内容:
这里的问题是,对于不属于 key 一部分的
ValueGeneratedOnAdd标识列(即),is反过来又使方法将它们标记为已修改,这又会生成错误的命令。AfterSaveBehaviorSaveUpdateUPDATE要解决这个问题,您必须
AfterSaveBehavior像这样设置(内部OnModelCreating覆盖):...
AfterSaveBehavior3.x 中唯一的区别是现在你拥有的不是属性GetAfterSaveBehavior而是SetAfterSaveBehavior方法。要让 EF Core 始终从更新中排除该属性,请使用SetAfterSaveBehaviorwith 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); // <--
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1223 次 |
| 最近记录: |