EF Core SQLITE-SQLite错误19:“唯一约束失败

时间:2018-10-09 10:19:19

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

我正在学习实体框架核心,将其与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:“唯一约束失败

我不知道怎么了。当我手动将数据插入数据库(使用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字段, 但是User实体不能有Messages,因为有一次他是“ 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,我在其中重写添加方法

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

顺便说一句,我不知道是否可以,但是我有0项要更新(!?) enter image description here

3 个答案:

答案 0 :(得分:3)

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

例如

// 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();

答案 1 :(得分:2)

您的问题(非常详细)缺少的是传递给Message的{​​{1}}的组成。您之前是否已将两个Insert()保存到数据库中,还是两个新对象?如果它们是新的,则其ID应为零,以便EF知道在创建User之前先创建它们。

顺便说一句-抱歉,这是一个“答案”而不是评论-S / O的怪异规则意味着我没有足够的声誉来添加评论...

答案 2 :(得分:2)

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

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

所以我通过在一个Business-Transaction上使用一个上下文(而不是一个数据库事务)解决了问题。刚刚将创建上下文转移到业务逻辑中,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;
            }
        }

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