【问题标题】:duplicate key value violates unique constraint EF core重复键值违反唯一约束 EF 核心
【发布时间】:2021-06-21 21:28:29
【问题描述】:

这是因为我有

public class Language
    {
        [Column("id")]
        public int Id { get; set; }
        [Column("name")]
        public string Name { get; set; }
        [Column("native_name")]
        public string NtiveName { get; set; }
        [Column("code_1")]
        public string Code1 { get; set; }
        [Column("code_2")]
        public string Code2 { get; set; }
    }
      public class UserCreateModel
    {
        [Required]
        public string FirstName { get; set; }
        [Required]
        public string LastName { get; set; }
        [Required]
        public string UserName { get; set; }
        [EmailAddress]
        [Required]
        public string VerificationEmail { get; set; }
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
        [Required]
        public bool IsBusinessAccount { get; set; }
        [Required]
        public Language Language { get; set; }
    }

这两个模型,当尝试创建它已经存在的语言时,我可以防止在语言表中创建语言并且只对其进行引用吗?

【问题讨论】:

  • 要么使用Attach,要么使用数据库查询语言并使用返回的实体。

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


【解决方案1】:

创建一对多关系。将 Id 和 LanguageId 属性添加到 UserCreateModel

public class UserCreateModel
    { 
         [Key]
         public int Id {get; set;}

        .....

        [Required]
        public int? LanguageId { get; set; }

       [ForeignKey(nameof(LanguageId))]
        [InverseProperty("UserCreateModels")]
        public Language Language { get; set; }
    }

public class Language
    {
        [Column("id")]
        [Key]
        public int Id { get; set; }
        ....
        [InverseProperty(nameof(UserCreateModel.Language))]
        public virtual ICollection<UserCreateModel> UserCreateModels { get; set; }
    }

【讨论】:

    【解决方案2】:

    您似乎正在将实体(语言)与视图模型混合使用。 (用户创建模型)

    UserCreateModel 通常根本不需要语言实体或对象引用。这可能是您从 Languages 实体加载的下拉列表中选择的内容,在这种情况下,我通常建议在 UserCreateModel 中只包含一个 LanguageId。

    然后,当您从 UserCreateModel 创建用户实体时,您的代码将如下所示:

    using (var context = new AppDbContext())
    { 
        Language language = context.Languages.Single(x => x.LanguageId == userCreateModel.LanguageId);
    
        User user = new User
        {
             FirstName = userCreateModel.FirstName,
             LastName = userCreateModel.LastName,
             UserName = userCreateModel.UserName,
             Language = language
        };
        context.Users.Add(user);
        context.SaveChanges();
    }
    

    您的当前视图模型中可能有一个看起来像语言实体的东西,但是当从您的视图反序列化时,它是对看起来像语言实体的数据的全新引用。 DbContext 对此一无所知。您可以通过在 DbContext 上使用 Attach 将 Language 对象与 DbContext 关联来解决此问题,但如果该 DbContext 可能已经加载/附加了该语言引用,您应该始终首先检查本地缓存中的现有引用,然后使用该参考。在这种情况下,我假设 DbContext 的范围在此代码之外,例如注入控制器并由 IoC 容器管理:

    Language language = context.Languages.Local.SingleOrDefault(x => x.LanguageId == userCreatedModel.Language.LanguageId);
    
    if (language == null)
    {
        context.Attach(userCreatedModel.Language);
        language = userCreatedModel.Language;
    }
    User user = new User
    {
        FirstName = userCreateModel.FirstName,
        LastName = userCreateModel.LastName,
        UserName = userCreateModel.UserName,
        Language = language
    };
    context.Users.Add(user);
    context.SaveChanges();
    

    这里的区别是我们首先检查.Local DbSet 缓存以获取对该语言的引用。这不会影响数据库。如果我们没有找到一种语言,我们可以附加提供的语言并关联该语言。否则我们将使用本地的。这仍然不是万无一失的,因为如果传递给调用的语言不存在,我们仍然可以获得异常。 (被其他请求删除,或被篡改无效)

    如果 User 实体公开了 LanguageId FK 属性,那么您可以放弃加载 Language 并只使用 LanguageId = userCreateModel.LanguageId 但是通过加载 Language 您可以确保创建的 User 实体(并可能返回等)具有完整且有效的语言参考集。在SaveChanges 之后,将更新任何 LanguageId FK 属性。如果传递了无效的 LanguageId,则通过加载该语言,您将在此时出现异常,指出未找到该语言。如果您只设置 LanguageId,您将在 SaveChanges 上的 User 上遇到 FK 约束冲突。

    【讨论】:

      猜你喜欢
      • 2022-11-19
      • 1970-01-01
      • 2016-12-23
      • 1970-01-01
      • 2022-01-13
      • 2020-03-27
      • 2013-06-23
      • 2016-06-12
      • 2013-03-15
      相关资源
      最近更新 更多