EF Core:如何避免重复条目?

EF Core: How to avoid duplicate entries?

我正在构建一个应用程序来保存图书清单。

添加一本书时,我正在检查该书的出版商和作者是否已存在于数据库中。如果答案是否定的,那么它就会被创建。但是,例如,如果一个发布者已经存在,我会从数据库中获取它并将其设置在传入的 Book 对象上以避免创建一个新条目,基本上具有相同的值但不同的 id。

但是,我在尝试执行此操作时遇到错误:

System.InvalidOperationException: The instance of entity type 'Publisher' cannot be tracked because another instance with the key value '{Id}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

我一直在努力弄清楚这个错误,但还没有找到答案(我在为 Authors 做同样的事情时遇到了同样的错误)。

下面是一些代码:

图书服务

public async Task<BookDto> AddBookAsync(BookDto book)
{
    await SetPublisherIfItAlreadyExists(book);
    await SetAnyAuthorsThatMayAlreadyExist(book);
    
    var bookEntity = _mapper.Map<Book>(book);
    var newBook = await _booksRepository.AddBookAsync(bookEntity);
    return _mapper.Map<BookDto>(newBook);
}

private async Task SetPublisherIfItAlreadyExists(BookDto book)
{
    if (book.Publisher?.Name == null) return;
    var publisher = await _publisherRepository.GetPublisherByNameAsync(book.Publisher.Name);
    if (publisher == null) return;
    book.Publisher = _mapper.Map<PublisherDto>(publisher);
}

private async Task SetAnyAuthorsThatMayAlreadyExist(BookDto book)
{
    if (!book.Authors.Any()) return;
    
    var existingAuthors = new List<Author>();
    var nonExistingAuthors = new List<AuthorDto>();

    foreach (var incomingAuthor in book.Authors)
    {
        var author = await _authorsRepository.GetAuthorByNameAsync(incomingAuthor.FirstName, incomingAuthor.LastName);
        if (author != null)
        {
            existingAuthors.Add(author);
        }
        else
        {
            nonExistingAuthors.Add(incomingAuthor);
        }
    }

    if (!existingAuthors.Any()) return;
    var existingAuthorsDtos = _mapper.Map<List<AuthorDto>>(existingAuthors);
    book.Authors = existingAuthorsDtos.Concat(nonExistingAuthors).ToList();
}

PublisherRepository

public async Task<Publisher> GetPublisherByNameAsync(string publisherName)
{
    var publisher = await _dbContext.Publishers.FirstOrDefaultAsync(p => p.Name.Equals(publisherName));
    return publisher;
}

作者资料库

public async Task<Author> GetAuthorByNameAsync(string firstName, string lastName)
{

    var author = await _dbContext.Authors.FirstOrDefaultAsync(a =>
        a.FirstName.ToLower().Equals(firstName.ToLower()) &&
        a.LastName.ToLower().Equals(lastName.ToLower())
    );
    return author;
}

图书资料库

public async Task<Book> AddBookAsync(Book book)
{
    await _dbContext.Books.AddAsync(book);
    await _dbContext.SaveChangesAsync();
    return book;
}

错误是如何触发的(更新)

为简单起见,这里是 HTTP 请求中包含的简化负载。假设数据库在第一次请求时为空

第一个请求

// POST /books
{
   title: "book title",
   publisher: "sunny publications"
   ... // other properties
}

第二次请求

// POST /books
{
   title: "some other book",
   publisher: "sunny publications"
   ... // other properties
}

第二个请求导致抛出所述错误。

我在这里错过了什么?将不胜感激!

您获取发布者(如果存在),这将开始跟踪该实体。

然后您将其映射到 PublisherDto 以将其分配给您的 BookDto,并将 BookDto 映射回实体,包括出版商。这将创建具有相同 Id 的第二个 Publisher 实例。

不要映射再映射,在实体上创建实体。