【问题标题】:EFCore Cannot insert the value NULL into columnEFCore 无法将值 NULL 插入列
【发布时间】:2021-10-31 15:03:47
【问题描述】:

我正在接受来自用户的 3 个字段。所有 3 个字段都必须输入。看看下面的实体配置——EntityConfiguration 中的所有 3 个字段都被标记为Required

当我将新记录保存到数据库时,这非常有效。但是,现在我需要更新一个条目。在这种情况下,如果我只想更新UserNamePassword - 会抛出异常,因为Age 不能是Null。我该如何解决这个问题?

InnerException = {"Cannot insert the value NULL into column 'Age', table 'DBFFFF.dbo.Users'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}

代码

public class User
{
    public int Id{ get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Age { get; set; }
}

public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("Users");

        builder.HasKey(c => c.Id);

        builder.Property(c => c.UserName)
                       .IsRequired(true);

        builder.Property(c => c.Password )
            .IsRequired(true);

        builder.Property(c => c.Age )
            .IsRequired(true); 
    }
}
    public async Task<string> UpdateUs(User u)
    {
        _dbCont.Update(u);
        await _dbCont.SaveChangesAsync();
        return "ok";
    }

【问题讨论】:

  • 如何更新实体?该代码比配置更相关
  • 我已经更新了我的代码。请看一下
  • 这不是问题,你发送什么,EF 会更新它。我的意思是您将此值作为空值发送,并且它知道您想将此字段更新为空值,我认为这是一个正常的过程。
  • builder.Property(c =&gt; c.Password ) -- 不要将用户的密码存储为明文!!!!!您必须对它们进行哈希和加盐。
  • @Dai 是的。它是盐渍和散列的。

标签: c# .net-core entity-framework-core


【解决方案1】:

你的问题在这里:

    public async Task<string> UpdateUs(User u)
    {
        _dbCont.Update(u);
        await _dbCont.SaveChangesAsync();
        return "ok";
    }

我假设您的UpdateUs 呼叫站点如下所示:

User u = new User()
{
    Id = 123,
    UserName = "foobar"
};

await UpdateUs(u);
  • 请注意,上述代码没有为AgePassword 设置任何值

    • 并且您不得将用户的密码存储为明文!
  • ...但是您的代码调用DbContext.Update...

    • ...它基本上告诉 EF “这个对象 u 现在表示带有 Id = 123 的实体的当前状态 包括 Age = null”这一事实,即 不是你想要的。
      • 我假设您真正想要的是 EF 仅将 UserName 属性视为已更新,并忽略尚未设置的其他非键属性(PasswordAge)。
  • 另外,我感觉您将实体类用作可变视图模型(例如,在 ASP.NET 或 WPF 中)。 您不应该这样做,因为实体对象代表持久的业务数据状态,它不代表内存中的可变 UI 状态。

    • 此外,视图模型需要实体类型没有的附加注释和成员。例如,“编辑用户”视图模型需要两个密码字段(“新密码”和“确认新密码”),这就是为什么我们有单独的视图模型类即使它们具有相同的成员一个实体类

无论如何,正确的解决方案(假设您仍然想使用您的 class User 作为内部 DTO)是使用 Attach 并在您的属性上设置 IsModified正在更新。

像这样:

    public async Task< UpdateUserAsync(User u)
    {
        // Preconditions:
        if( u is null ) throw new ArgumentNullException(nameof(u));
        if( u.Id == default ) throw new ArgumentException( message: "User's Id is not set.", paramName: nameof(u) );

        // 

        this.dbContext.Users.Attach( u );

        var entry = this.dbContext.Entry( u );
        if( u.UserName != null ) entry.Property( x => x.UserName ).IsModified = true;
        if( u.Age      != null ) entry.Property( x => x.Age      ).IsModified = true;

        this.dbContext.Configuration.ValidateOnSaveEnabled = false; // <-- Only do this if necessary.

        Int32 rowCount = await this.dbContext.SaveChangesAsync();
        if( rowCount != 1 ) throw new InvalidOperationException( "No rows (or too many rows) were updated." );
    }

【讨论】:

  • 哇,谢谢。除了我遇到的问题,这里还有更多细节。谢谢你的详细回答让我学到了很多。
  • 只是一个简单的问题。有没有办法找到对象的哪些属性是 Null 并设置 IsModified - true; ?例如,有时 Age 可以为空,而其他时候 Password 或 UserName 可以为空。有没有办法检测到这些?
  • @Illep 如果Age 可以是null 那么为什么它被标记为Required(即NOT NULL)?正如我所说,您应该将实体类用作视图模型。听起来您应该创建一个单独的 class UserDetailsForm 并编写一个将其映射到(和从)您的 User 实体对象的方法。
【解决方案2】:

我认为这将是有用的答案。更新时可以忽略特定字段

public async Task<string> UpdateUser(User u)
{
    _dbCont.Update(u);
    _dbCont.Entry(u).Property(x => x.Age).IsModified = false;
    await _dbCont.SaveChangesAsync();
    return "ok";
}

【讨论】:

    【解决方案3】:

    通过以下配置,您将 Age 字段设置为不可为空。

    builder.Property(c => c.Age ).IsRequired(true); 
    

    删除IsRequired(true);配置应该如下。

    builder.Property(c => c.Age ); 
    

    【讨论】:

    • 我想保持它不可为空。当我保存记录时,这对我非常有用。有没有办法临时启用字段 Nullable ?
    • OP 想让专栏NULLable,他们想阻止EF做UPDATE SET Age = NULL
    • @Dai 是的。确切地。这是我需要做的
    猜你喜欢
    • 1970-01-01
    • 2013-02-27
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多