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 实例。
不要映射再映射,在实体上创建实体。
我正在构建一个应用程序来保存图书清单。
添加一本书时,我正在检查该书的出版商和作者是否已存在于数据库中。如果答案是否定的,那么它就会被创建。但是,例如,如果一个发布者已经存在,我会从数据库中获取它并将其设置在传入的 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 实例。
不要映射再映射,在实体上创建实体。