【问题标题】:EF Code First ApplicationUser required DateTime not addedEF Code First ApplicationUser 需要 DateTime 未添加
【发布时间】:2021-09-23 06:33:51
【问题描述】:

我在使用新的 ASP.NET Core 应用程序(代码优先)注册新用户时遇到问题。
注册时,我使用的是继承自 `IdentityUser` 的 `ApplicationUser`。
public class ApplicationUser : IdentityUser
{
    [Required]
    [DefaultValue(5)]
    public int ViewerRange { get; set; }
    [DefaultValue(false)]
    [Required]
    public bool IsTACAccepted { get; set; }
    public DateTime? TACAccepted { get; set; }
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public DateTime Created { get; set; }
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime Modified { get; set; }
    public string Role { get; set; }

    public ApplicationUser()
    {
        Modified = DateTime.Now;
        Created = DateTime.Now;
    }
}

这是 Register.cshtml.cs 的一部分

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl ??= Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser
        {
            UserName = Input.Email,
            Email = Input.Email,
            Role = Input.Role,
            Created = DateTime.Now,
            Modified = DateTime.Now,
            IsTACAccepted = false,
            ViewerRange = 5
        };


        var result = await _userManager.CreateAsync(user, Input.Password);
        //[...]

注册时,CreateAsync 方法失败并出现以下错误:

InvalidOperationException: The value for property 'ApplicationUser.Modified' cannot be set to null because its type is 'System.DateTime' which is not a nullable type.

我不明白为什么会失败。
谁能详细解释一下?
亲切的问候

【问题讨论】:

  • 您将Modified 标记为DatabaseGeneratedOption.Computed,这意味着它的值应该被忽略,但您需要设置一个显式值。也没有迹象表明该值将如何生成 - 触发器?默认约束?这不是唯一的怪事。 DatabaseGeneratedOption.Identity Created 没有意义。自动递增值仅适用于数字字段
  • 你想做什么?你想如何处理这些列?是否要指定显式值?他们应该获得默认值吗?在那里面。如果您需要添加例如默认约束和/或触发器来填充这些列。
  • 如果您想自己指定值,请删除DatabaseGenerated 属性并将它们用作普通字段。您可以通过指定默认属性值来避免显式分配,例如public DateTime Created { get; set; } = DateTime.Now。这不会在数据库中生成DEFAULT 约束或触发器

标签: asp.net-core asp.net-identity


【解决方案1】:

错误的原因是[DatabaseGenerated(DatabaseGeneratedOption.Computed)] 说它的值是由数据库生成的,所以任何分配的值都应该被忽略。错误表明情况并非如此。

这不是代码中唯一的奇怪之处。 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 在 DateTime 上毫无意义。 IDENTITY 用于自动递增数字字段。这对 DateTime 列没有任何意义。

如果您想自己指定值,请删除 DatabaseGenerated 属性并将它们用作普通字段。您可以通过指定默认属性值来避免显式分配,例如

public DateTime Created { get; set; } = DateTime.Now;
public DateTime Modified { get; set; } = DateTime.Now;

也可以配置 数据库 来生成这些值。这在文档中的Generated Values 文章中进行了描述。添加CreatedModified 列在Date/time generation 部分中进行了描述。

Created 列的默认值约束可以用HasDefaultValueSql 指定:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Created)
        .HasDefaultValueSql("getdate()");
}

Modified 时间戳需要一个触发器,但不能在 DbContext 的模型中指定。这必须在数据库中直接创建:

CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
    AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

    DECLARE @Id INT

    SELECT @Id = INSERTED.BlogId
    FROM INSERTED

    UPDATE dbo.Blogs
    SET LastUpdated = GETDATE()
    WHERE BlogId = @Id
END

或者通过执行相同脚本的原始 SQL 迁移:

migrationBuilder.Sql(
@"
    EXEC ('CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
    AFTER UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

    DECLARE @Id INT

    SELECT @Id = INSERTED.BlogId
    FROM INSERTED

    UPDATE dbo.Blogs
    SET LastUpdated = GETDATE()
    WHERE BlogId = @Id
END')");

【讨论】:

    【解决方案2】:
    public DateTime? Created { get; set; }
    public DateTime? Modified { get; set; }
    

    使用这个,你的模型需要被告知你的数据库中可以有空值,否则它会假设没有。

    更新

    **I did not actually carefully noticed your error. Now I will update my answer:-**

    我看到您没有在控制器中使用此属性。所以最初这个属性可以为空。因为你发现了这个问题。首先,使用该属性忽略可空值。

    试试这样:-

    public DateTime TACAccepted { get; set; }  = DateTime.Now;
    

    你可以试试这个,因为你的属性是空的。您可以像上面的代码一样设置此属性以忽略可空值。或者,如果不需要,则从模型中删除此属性。我想它会解决你的问题。

    另外,请确保该列在 SQL 数据库中声明为 DateTime。

    【讨论】:

    • 错误表明应用程序试图插入 NULL。 应用程序应该固定为始终提供一个值,而不是在数据库中允许NULL
    • @PanagiotisKanavos 哎呀!!我实际上并没有仔细注意到这一点。我认为他没有使用 TACAccepted 这个属性并且它可以为空。我会更新我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 2015-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多