【发布时间】:2020-12-30 19:28:34
【问题描述】:
我正在构建一个应用程序来保存图书目录。
添加图书时,我正在检查该图书的出版商和作者是否已存在于数据库中。如果答案是否定的,那么它就会被创建。但是,例如,如果一个出版商已经存在,我会从数据库中获取它并将其设置在传入的 Book 对象上,以避免创建一个新条目,基本上具有相同的值但不同的 id。
但是,我在尝试执行此操作时遇到错误:
System.InvalidOperationException:实体类型的实例 无法跟踪“发布者”,因为另一个具有密钥的实例 值“{Id}”已经存在 跟踪。附加现有实体时,确保只有一个实体 附加了具有给定键值的实例。
我一直试图弄明白这个错误,但还没有找到答案(我在为作者做同样的事情时遇到同样的错误)。
以下是部分代码:
图书服务
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;
}
AuthorsRepository
>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
}
第二个请求会引发所描述的错误。
我在这里缺少什么?将不胜感激!
【问题讨论】:
-
那么到底是哪里抛出的异常呢?该错误并不意味着您的数据库中有一些 dup 实体,它位于包含 dup 跟踪实体的
DbContext中(通过实体键属性标识) -
@Hopeless 让我更新问题以包含触发错误的示例
-
我一直遇到这类问题。我的建议是,当您只是从数据库中选择(例如,在您的情况下,您只是按姓名选择作者以查看他是否存在),添加此
AsNoTracking()。例如,在您的GetAuthorByNameAsync中,看看是否有帮助
标签: c# asp.net-core entity-framework-core webapi