实体核心更新时违反PRIMARY KEY约束错误

时间:2018-04-16 14:07:59

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

书籍和作者之间存在着许多典型的关系。 我尝试更新作者时遇到错误。错误的全名是" SqlException:违反PRIMARY KEY约束' PK_BookAuthor'。无法在对象' dbo.BookAuthor'中插入重复键。重复键值为(2,2)。"

如果我不使用UpdateAssociatedObject函数,一切都没问题。但是如果我使用它,当我尝试保存更改时会收到错误。

 var parseBook = Mapper.Map<BookViewModel, Book>(viewModel);
        Book viewModelBook = parseBook;
        var book = db.Books.FindOne(viewModel.BookId);
        book.BookId = viewModelBook.BookId;
        book.BookName = viewModelBook.BookName;
        book.Genre = viewModelBook.Genre;
        book.Pages = viewModelBook.Pages;
        book.Publisher = viewModelBook.Publisher;
        db.SaveChanges();
        UpdateAssociatedObject(book, viewModelBook);
    }
    public void UpdateAssociatedObject(Book bookForUpdate, Book book)
    {
        bookForUpdate.BookAuthors = book.BookAuthors;
    //error is here
        db.SaveChanges();
    }

映射后viewModelBook的值: The value of BookAuthor property 模型(为简洁起见,删除了一些属性)

 public class Book
{
    public int BookId { get; set; }

    public List<BookAuthor> BookAuthors { get; set; }
}
public class Author
    {
        public int AuthorId { get; set; }
        public string AuthorName { get; set; }
        public List<BookAuthor> BookAuthors { get; set; }
    }
 public class BookAuthor
{
    public int BookId { get; set; }
    public Book Book { get; set; }

    public int AuthorId { get; set; }
    public Author Author { get; set; }
}
public class BookViewModel
{
    public int BookId { get; set; }

    public virtual List<AuthorViewModel> Authors { get; set; }    
}
public class AuthorViewModel
    {
        public int AuthorId { get; set; }

        public string AuthorName { get; set; }
    }
public class Library : DbContext
{
    public DbSet<Book> Books { get; set; }

    public DbSet<Author> Authors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BookAuthor>().HasKey(sc => new { sc.BookId, sc.AuthorId });
    }
}

映射器:

 CreateMap<BookViewModel, Book>()
            .ForMember(b => b.BookId, opt => opt.MapFrom(b => b.BookId))
        .PreserveReferences()
        .ForMember(b => b.BookAuthors, opt => opt.MapFrom(b => b.Authors
        .Select(a => new BookAuthor { AuthorId = a.AuthorId })));

UPD: BookId在创建图书后跳转(步长为1000)。

将Include()添加到Find方法后,错误已更改

System.InvalidOperationException:&#39;实体类型的实例&#39; BookAuthor&#39;无法跟踪,因为已经跟踪了另一个具有相同键值的实例{&#39; BookId&#39;,#39; AuthorId&#39;}。附加现有实体时,请确保仅附加具有给定键值的一个实体实例。考虑使用&#39; DbContextOptionsBuilder.EnableSensitiveDataLogging&#39;查看冲突的键值。&#39;

2 个答案:

答案 0 :(得分:1)

最后它有效!您可以在下面看到与原始代码的差异。

P.S也许你知道更好的删除方法算法,如果是这样你可以在评论中输入它然后我会改变答案。

BookRepository:

 public Book Find(int id)
    {
        Book searchedBook = db.Books
          .Include(b => b.BookAuthors)
          .ThenInclude(b => b.Author)
          .SingleOrDefault(b => b.BookId == id);

        return searchedBook;
    }

<强> bookService的:

public void UpdateObject(BookViewModel viewModel)
        {
            Book parsedBook = Mapper.Map<BookViewModel, Book>(viewModel);
            //Book viewModelBook = parseBook;
            var book = db.Books.Find(viewModel.BookId);
            book.BookId = parsedBook.BookId;
            book.BookName = parsedBook.BookName;
            book.Genre = parsedBook.Genre;
            book.Pages = parsedBook.Pages;
            book.Publisher = parsedBook.Publisher;
            UpdateAssociatedObject(book, parsedBook);
        }
public void UpdateAssociatedObject(Book bookToUpdate, Book viewModelBook)
        {
            AddUpdatedAuthors(bookToUpdate, viewModelBook);
            RemoveUpdatedAuthors(bookToUpdate, viewModelBook);
            db.SaveChanges();
        }
     private void AddUpdatedAuthors(Book bookToUpdate, Book viewModelBook)
        {
            foreach (var authors in viewModelBook.BookAuthors)
            {
                var searchBookAuthor = bookToUpdate.BookAuthors.Find(b => b.AuthorId == authors.AuthorId);
                if (searchBookAuthor == null)
                {
                    bookToUpdate.BookAuthors.Add(authors);
                }
            }
        }
        private void RemoveUpdatedAuthors(Book bookToUpdate, Book viewModelBook)
        {
            int countOfAuthors = bookToUpdate.BookAuthors.Count;
            for (int i = 0; i < countOfAuthors; i++)
            {
                BookAuthor searchBookAuthor = null;
                foreach (var viewModelAuthors in viewModelBook.BookAuthors)
                {
                    if (viewModelAuthors.AuthorId == bookToUpdate.BookAuthors[i].AuthorId)
                    {
                        searchBookAuthor = viewModelAuthors;
                    }
                }
                if (searchBookAuthor == null)
                {
                    bookToUpdate.BookAuthors.Remove(bookToUpdate.BookAuthors[i]);
                    i--;
                    countOfAuthors--;
                }
            }
        }

答案 1 :(得分:0)

问题在于使用db.SaveChanges()。您在

中多次调用具有相同上下文的db.SaveChanges
db.SaveChanges();

UpdateAssociatedObject(book, viewModelBook);

并在UpdateAssociatedObject(book, viewModelBook); 尝试删除一个db.SaveChanges()。