【问题标题】:EF Core SQLITE - SQLite Error 19: 'UNIQUE constraint failedEF Core SQLITE - SQLite 错误 19:'UNIQUE 约束失败
【发布时间】:2018-10-09 10:19:19
【问题描述】:

我正在学习实体框架核心,将它与 SQLITE 一起使用 我在数据库中有 2 个表。

消息:

CREATE TABLE `Messages` (
    `Id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    `IsDeleted` INTEGER NOT NULL,
    `FromUserId`    INTEGER,
    `ToUserId`  INTEGER,
    `SendDate`  TEXT NOT NULL,
    `ReadDate`  TEXT NOT NULL,
    `MessageContent`    TEXT,
    CONSTRAINT `FK_Messages_Users_ToUserId` FOREIGN KEY(`ToUserId`) REFERENCES `Users`(`Id`) ON DELETE RESTRICT,
    CONSTRAINT `FK_Messages_Users_FromUserId` FOREIGN KEY(`FromUserId`) REFERENCES `Users`(`Id`) ON DELETE RESTRICT
);

和用户表:

CREATE TABLE `Users` (
    `Id`    INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    `IsDeleted` INTEGER NOT NULL,
    `Name`  TEXT,
    `DisplayName`   TEXT,
    `Gender`    INTEGER NOT NULL,
    `BirthDate` TEXT NOT NULL
);

我的 c# 类如下所示:

public class User
{
    public int Id {get;set;}
    public bool IsDeleted{get;set;}
    [Required]
    public string Name{get;set;}

    public string DisplayName{get;set;}

    [Required]
    public char Gender{get;set;}

    [Required]
    public DateTime BirthDate{get;set;}

    public User(string n, string dn, char g, DateTime bd)
    {
        Name=n; DisplayName = dn; Gender = g; BirthDate = bd;
    }

    protected User(){}

    public override string ToString()
    {
        return string.Format("{0} ({1}) {2}", this.Name, this.Gender,this.BirthDate.ToShortDateString());
    }
}

和消息类:

public class Message 
{
    public int Id{get;set;}
    public bool IsDeleted{get;set;}
    [Required]
    public Users.User FromUser{get;set;}
    
    [Required]
    public Users.User ToUser{get;set;}

    [Required]
    public DateTime SendDate{get;set;}
    public DateTime? ReadDate{get;set;}

    [Required]
    public string MessageContent{get;set;}

    protected Message(){}

    public Message(User from, User to, string content)
    {
        this.FromUser = from;
        this.ToUser = to;
        this.MessageContent = content;
        this.SendDate = DateTime.Now;
        this.ReadDate = DateTime.Now;
    }

}

users 表有效,但是当我尝试通过以下方式向消息添加新实体时:

public override int Insert(Message entity)
{
    dbc.Messages.Add(entity);
    dbc.SaveChanges();
    return entity.Id;
}

我收到以下错误:

SQLite 错误 19: 'UNIQUE 约束失败

我不知道出了什么问题。当我手动将数据插入数据库(使用 SQLite 的 DB 浏览器)时,它可以工作。 类中的关系好吗?

我的dbContextSnapshot 是:

[DbContext(typeof(RandevouDbContext))]
partial class RandevouDbContextModelSnapshot : ModelSnapshot
{
    protected override void BuildModel(ModelBuilder modelBuilder)
    {
#pragma warning disable 612, 618
    modelBuilder.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");

    modelBuilder.Entity("RandevouData.Messages.Message", b =>
        {
            b.Property<int>("Id")
                .ValueGeneratedOnAdd();

            b.Property<int>("FromUserId");

            b.Property<bool>("IsDeleted");

            b.Property<string>("MessageContent")
                .IsRequired();

            b.Property<DateTime?>("ReadDate");

            b.Property<DateTime>("SendDate");

            b.Property<int>("ToUserId");

            b.HasKey("Id");

            b.HasIndex("FromUserId");

            b.HasIndex("ToUserId");

            b.ToTable("Messages");
        });

    modelBuilder.Entity("RandevouData.Users.User", b =>
        {
            b.Property<int>("Id")
                .ValueGeneratedOnAdd();

            b.Property<DateTime>("BirthDate");

            b.Property<string>("DisplayName");

            b.Property<char>("Gender");

            b.Property<bool>("IsDeleted");

            b.Property<string>("Name")
                .IsRequired();

            b.HasKey("Id");

            b.ToTable("Users");
        });

    modelBuilder.Entity("RandevouData.Messages.Message", b =>
        {
            b.HasOne("RandevouData.Users.User", "FromUser")
                .WithMany()
                .HasForeignKey("FromUserId")
                .OnDelete(DeleteBehavior.Cascade);

            b.HasOne("RandevouData.Users.User", "ToUser")
                .WithMany()
                .HasForeignKey("ToUserId")
                .OnDelete(DeleteBehavior.Cascade);
        });
#pragma warning restore 612, 618
}

顺便说一句。我无法从两侧配置映射。

Message 实体具有 UserFrom 和 UserTo 字段, 但是用户实体不能有消息,因为他一次是“userFrom”,另一次他可以是“userTo”。

还有一个代码,我在其中创建消息实体

        public int SendMessage(int senderId, int receiverId, string content)
    {
        var dao = new MessagesDao();
        var usersDao = new UsersDao();
        var userService = new UserService(mapper);
        
        var sender = usersDao.Get(senderId);
        var receiver = usersDao.Get(receiverId);
        
        var entity = new Message(sender,receiver,content);
        var id = dao.Insert(entity);
        return id;
    }

我覆盖 GET 方法的用户 dao

 public override User Get(int id)
        {
            using (var context = new RandevouDbContext())
            { 
                var user = dbc.Find<User>(id);
                return user;
            }
        }

和 MessagesDao 我覆盖 Add 方法

 public override int Insert(Message entity)
        {
            using (var dc = new RandevouDbContext())
            {
                dc.Messages.Add(entity);
                dc.SaveChanges();
                return entity.Id;
            }
        }

顺便说一句,我不知道是否可以,但我有 0 个条目要更新(!?)

【问题讨论】:

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


    【解决方案1】:

    要修复“唯一”约束异常,您需要确保您正在使用的所有实体都被您要保存到的上下文跟踪。

    例如

    // Get user from one context
    
    User thisUser = db.Users.Find(3);
    
    //Now imagine you have another context which has been created later on
    
    DbContext newContext = new DbContext();
    
    //and you want to create a message, assign it to your user 
    //and save it to the new context
    
    Message newMessage = new Message
    {
        Text = "Hello",
        User = thisUser
    }
    
    newContext.Messages.Add(newMessage);
    
    // saving this to newContext will fail because newContext is not tracking thisUser,
    // Therefore attach it...
    
    newContext.Users.Attach(thisUser).
    
    //now newContext is tracking thisUser *AND* newMessage, so the save operation will work
    
    newContext.SaveChanges();
    

    【讨论】:

      【解决方案2】:

      好的,我解决了问题。 阅读一些关于 EF DbContext 的文章。 当我为 GET User 和 CREATE 消息使用了另外两个上下文时, 实体(user1 和 user2)与我用于第二个上下文的实体不同。我的意思是,这些实体是一样的,但他们有另一个跟踪

      EF Core / Sqlite one-to-many relationship failing on Unique Index Constraint

      所以我通过在一个业务事务(不是一个数据库事务)上使用一个上下文来解决问题。刚刚将创建上下文转移到业务逻辑中,Generic dao 类在构造函数中等待上下文,如下所示:

      public class MessagesDao : BasicDao<Message>
          {
              public MessagesDao(RandevouDbContext dbc) : base(dbc)
              {
      
              }
              public IQueryable<Message> QueryMessages()
              {
                      return dbc.Messages.AsQueryable();
      
              }
      
              public override int Insert(Message entity)
              {
                  dbc.Messages.Add(entity);
                  dbc.SaveChanges();
                  return entity.Id;
      
              }
          }
      
      
      
       public abstract class BasicDao<TEntity> where TEntity : BasicRandevouObject
          {
              protected RandevouDbContext dbc;
              public BasicDao(RandevouDbContext dbc) { this.dbc = dbc; }
      
              public virtual int Insert(TEntity entity)
              {
                      dbc.Add<TEntity>(entity);
                      dbc.SaveChanges();
                      return entity.Id;
      
              }
      
              public virtual void Update(TEntity entity)
              {
                      dbc.Update(entity);
                      dbc.SaveChanges();
      
              }
      
              public virtual void Delete(TEntity entity)
              {
                      dbc.Remove(entity);
                      dbc.SaveChanges();
      
              }
      
              public virtual TEntity Get(int id)
              {
                      var entity = dbc.Find<TEntity>(id);
                      return entity;
      
              }
      
      
          }
      

      在业务逻辑代码中:

      public int SendMessage(int senderId, int receiverId, string content)
              {
                  using (var dbc = new RandevouDbContext())
                  { 
                      var dao = new MessagesDao(dbc);
                  var usersDao = new UsersDao(dbc);
                  var userService = new UserService(mapper);
      
                  var sender = usersDao.Get(senderId);
                  var receiver = usersDao.Get(receiverId);
      
                  var entity = new Message(sender,receiver,content);
                  var id = dao.Insert(entity);
                  return id;
                  }
              }
      

      现在一切正常。顺便说一句,它仍然是脏代码,因为我只是在尝试一些东西,但我希望如果有人遇到类似的问题,那么他会找到这篇文章。

      【讨论】:

      • 谢谢你!我在使用带有 blazor .NET 6 应用程序的 Sqlite 时遇到了同样的问题。你在上面的帖子帮助我理解了。我(错误地)将我的服务添加为 Transient,将其更改为 Scoped 解决了我的问题。谢谢!
      【解决方案3】:

      您的(非常详细的)问题中缺少的是您传递给Insert() 的Message 的构成。您之前是否将这两个Users 保存到数据库中,或者它们也是两个新对象?如果它们是新的,那么它们的 ID 应该为零,以便 EF 知道在创建 Message 之前先创建它们。

      【讨论】:

      • 啊 - 在那种情况下,您确定获取它们并将两个用户组合到消息字段中吗?您应该将Message._xx_User 字段设置为从保存请求返回的组合用户,或者执行.Get() 以检索组合的User 记录并将它们设置在Message 记录中。
      • 我不明白。在我创建消息之前,我已经为两个用户做了 dao.Get(userId)。接下来,我创建了带有(用户 u1,用户 u2,字符串内容)的消息。在用户类上我没有消息属性。
      • 听起来你正在按照我的建议去做。您是否可以扩展您的原始问题并显示保存用户、让他们回来、然后保存消息的代码?
      • 您在 Insert() 中拥有的 dbcontext (dbc) 是在其他地方构建的 - 它是否与用于保存用户和消息的上下文相同?如果它不是相同的上下文,它是否在保存用户更改之前初始化?如果是这样,那么它不知道新的用户记录。一般来说,上下文应该是非常短暂的;我会在 Insert() 方法中构建一个新的上下文...
      • 即使我一直创建新的数据库上下文,仍然存在同样的问题:SqliteException:SQLite 错误 19:'UNIQUE 约束失败:Users.Id'。我还在课程中阅读过不要一直创建新的 DbContext
      猜你喜欢
      • 2021-12-28
      • 1970-01-01
      • 2020-10-18
      • 2021-10-06
      • 1970-01-01
      • 2018-07-26
      • 2021-11-02
      • 1970-01-01
      • 2012-03-14
      相关资源
      最近更新 更多