Reputation: 11464
I'm facing an issue when inserting records with Many to Many navigation property,
I have these models
//ModelBase
public class ModelBase
{
[Key]
public long Id { get; set; }
}
//Book
public class Book : ModelBase
{
public string Title { get; set; }
public virtual ICollection<Author> Authors { get; set; }
public virtual ICollection<PublishedBook> PublishedBooks { get; set; }
}
//Author
public class Author : ModelBase
{
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
And DTOs called BookDto
and AuthorDto
for transfer data between layers
From the controller I fill data to DTOs and call Create
method (which is in separate layer) to Save data to database,
BookDto bookDto = new BookDto()
{
Id = model.Id,
Title = model.Title,
Authors = model.AuthorIds.Select(c => new AuthorDto { Id = c }).ToList()
}
using (ServiceFactory facory = new ServiceFactory())
{
factory.Book.Create(bookDto);
}
In the Create
method I map DTOs with POCOs using ValueInjector
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
bookRepository.Add(book);
unitOfWork.Commit();
}
And it calls Add
method in Genaric Repository Base
public virtual void Add(T entity)
{
dbset.Add(entity);
}
This inserts data to Books
table and BookAuthors
tables appropriately BUT inserts new record into the Authors
table even if I pass Authors
which has existing AuthorIds
for Book.Authors
from the controller.
Any Idea how to fix this?
Upvotes: 1
Views: 388
Reputation: 177163
You are missing to attach the existing authors to the context. You could do it like so:
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
foreach (var author in book.Authors)
authorRepository.Attach(author);
bookRepository.Add(book);
unitOfWork.Commit();
}
where the generic Attach
methods is just implemented calling dbset.Attach(entity);
. I am assuming that all repositories (authorRepository
and bookRepository
) share the same context instance. Otherwise the above won't work.
The fact that the AuthorId
already exists in the database doesn't matter. EF doesn't check if the Id exists by a query first. If you call Add
on an object graph of detached entities it tries to insert the whole graph by generating INSERT statements for the parent and for all children. By attaching the children you effectively turn off the INSERT statements for those.
Upvotes: 2