【问题标题】:MVC5 EF6 Seed Method: Cannot insert NULL into column 'Id' table AspNetUsersMVC5 EF6 种子方法:无法将 NULL 插入列“Id”表 AspNetUsers
【发布时间】:2017-06-10 21:34:01
【问题描述】:

实体框架 6、MVC5、ASP 身份 2.0。

我已按照这 2 个教程创建 Web 应用程序的登录部分:part 1 - part 2

我从另一个库项目中的 IdentityUser 类创建了自己的 AppUser 类。我也有来自 IdentityDbContext 的数据上下文

    public class DataContext: IdentityDbContext<AppUser>
    {
       public DataContext(): base("DefaultConnection") { }

       protected override void OnModelCreating(DbModelBuilder modelBuilder)
       {
           modelBuilder.Entity<AppUser>().Property(u => u.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
           base.OnModelCreating(modelBuilder);
      }
    }

public class AppUser : IdentityUser
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    public override string Id { get; set; }

    [Required]
    [Display(Name ="First Name")]
    [MaxLength(100)]
    public string FirstName { get; set; }

    [Required]
    [Display(Name = "Last Name")]
    [MaxLength(100)]
    public string LastName { get; set; }

    [MaxLength(100)]
    public string Title { get; set; }

    [Required(ErrorMessage = "Email is required.")]
    [Display(Description = "Email")]
    public override string Email { get; set; }

    [MaxLength(20)]
    public string Phone { get; set; }

    [Range(0,9999)]
    [Display(Name = "Extension")]
    public int PhoneExtension { get; set; }

    [Display(Name = "Active")]
    [DefaultValue(true)]
    public bool IsActive { get; set; }
}

当我运行种子方法时,出现以下错误

System.Data.SqlClient.SqlException:无法将值 NULL 插入列 'Id'、表 'CRM1.dbo.AspNetUsers';列不允许空值。插入失败。 声明已终止。

播种方法如下:

protected override void Seed(CRM.DAL.Data.DataContext context)
    {
        context.AccessLevels.AddOrUpdate(al => al.Label,
            new Model.AccessLevel { AccessLevelID = 1, Label = "Admin", Description = "Full access to the CRM data" },
            new Model.AccessLevel { AccessLevelID = 2, Label = "Customer Service", Description = "Full access to the CRM data" },
            new Model.AccessLevel { AccessLevelID = 3, Label = "Sales", Description = "Full access to the CRM data" }
            );

        if (!(context.Users.Any(u => u.UserName == "admin@admin.com")))
        {
            var userStore = new UserStore<AppUser>(context);
            var userManager = new UserManager<AppUser>(userStore);
            var userToInsert = new AppUser
            {
                Id = "1",
                FirstName = "Admin",
                LastName = "Admin",
                Title = "Administrator",
                Email = "admin@admin.com",
                PhoneExtension = 0,
                IsActive = true,
                AccessLevelID = 1,
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                TwoFactorEnabled = false,
                LockoutEnabled = false,
                AccessFailedCount = 0,
                UserName = "admin"
            };
            userManager.Create(userToInsert, "password");
        }

    }

所以 Id 得到一个值,但 EF 说它正在插入 NULL。我尝试不给 Id 任何价值,认为 EF 会自动执行但不会。

用我的上下文播种 AspNetUser 的方法是什么?我看到了不同的教程,但它不适用于我扩展的 AppUser 类和 DataContext。

谢谢

【问题讨论】:

  • 尝试将 Id 的类型更改为 int 看看会发生什么
  • 您能否确认Id 列已创建为标识列?
  • @kkkkk 将 Id 更改为 int 表示它与从 IdentityUser 继承的 Id 的类型不匹配
  • @markpsmith Id 在查看数据库架构时未创建为标识列

标签: c# asp.net-mvc entity-framework asp.net-identity


【解决方案1】:

从您的代码中删除这一行:

modelBuilder.Entity<AppUser>().Property(u => u.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);

还有这部分来自您的User

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public override string Id { get; set; }

你不需要覆盖这个属性,只需使用父级的属性(实体框架会映射它)。

解释:当你像你一样配置Id属性时,你告诉实体框架属性值来自数据库,所以它完全忽略了你在插入时传递给这个属性的值进入数据库。

另一方面,如果你的数据库表中的列没有默认值,它会失败,因为实体框架没有为它提供值。

所以你需要决定:要么你的表必须有一个默认值,要么你应该删除那个字段的 HasDatabaseGeneratedOption 选项。

问题是IdentityUser 使用string 作为密钥,通常是Guid。由于 EF 不会自动为您生成 Id,并且您的列也不会生成,因此您可以为您的 User 创建一个 constructor,如下所示:

public AppUser()
{
    Id = Guid.NewGuid().ToString();
}

每当您创建一个新用户时,它都会有一个新的Guid 作为 Id....当您从上下文中读取现有用户时,虽然您的构造函数会在 EF 实例化时生成它,然后 EF 将加载Id 表中的值(忽略构造函数中提供的值)。

这意味着您的构造函数中自动生成的 Guid 将仅用于新实例。

【讨论】:

  • 我尝试仅删除该行,得到相同的结果。所以我也确实删除了 AppUser 中 Id 字段的覆盖,因为我使用了它的属性。然后它起作用了。未来,从前端,EF会不会自动生成Id?
  • @Greg 不,不会,但我编辑了我的答案,并为您的情况提供了可能的解决方案。
  • 出于某种原因,我尝试运行种子方法而不给我的用户提供要插入的 ID,而 EF 给了我 '3a66c954-393f-42e2-b810-36a044d7a0b2' ..... 所以我做到了不使用 Id = Guid.NewGuid().Tostring()。还是谢谢
  • 嗯,这是一个有趣的信息。它看起来 IdentityUser 为你生成它(根本不是 EF),虽然这是来自 AspNet.Core,但我认为它以前是这样的:https://github.com/aspnet/Identity/blob/5480aa182bad3fb3b729a0169d0462873331e306/src/Microsoft.AspNet.Identity.AspNetCoreCompat/IdentityUser.cs
【解决方案2】:

以下发生在我身上。我按照本指南将 ASP.NET Identity 中用户的主键从 Guid 更改为 Int。

https://docs.microsoft.com/en-us/aspnet/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity

我已经进行了一些迁移,因此 EF 尝试将 Id 从 nvarchar(128) 更改为 int。这有效,但Identity Specification 从未从“否”变为“是”。通过删除所有以前的迁移、删除数据库然后为所有更改进行新的迁移来解决它。由于我们处于开发的早期阶段,所以这不是问题。

【讨论】:

    猜你喜欢
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多