如何解决此错误:“无法跟踪实体类型的实例...”

时间:2019-12-09 20:05:17

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

这里是完整错误:

  

System.InvalidOperationException:无法跟踪实体类型'Book'的实例,因为已经跟踪了另一个键值为'{BookId:382234c3-721d-4f34-80e5-57657bbbbb32}'的实例。在附加现有实体时,请确保仅附加一个具有给定键值的实体实例。”

当我尝试从业务逻辑层中的服务向数据库添加新实体时,抛出该错误。我使用EF Core,在我的数据访问层中使用存储库和工作单元与DB进行交互。

以下是一些引发错误的代码,它们在存储库中:

public void Create(T entity)
{
    this.LibraryContext.Set<T>().Add(entity);
}

依次从这里调用它:

public bool TakeBookById(Guid bookId, Guid studentId, DateTime issueDate) // student takes the book from library
        {
            var book = mapper.Map<BookDto>(unit.Books.FindByCondition(x => x.BookId == bookId).FirstOrDefault()); // take that book for later using 

            if (book != null && book.IsAvailable == true) // if this book is available
            {
                var student = mapper.Map <StudentDto>(unit.Students.FindByCondition(x => x.StudentId == studentId).FirstOrDefault()); // look for that student
                if (student != null && student.LibraryCard.Fields.Where(x => x.Book.IsAvailable == false).Count() <= 10)
                { // if student exists and his/her limit of non-returned books is not exceeded
                    var libraryCard = mapper.Map<LibraryCardDto>(student.LibraryCard); // take his/her library card
                    unit.LibraryCardFields.Create( // create library card's field with current data
                        mapper.Map<LibraryCardField> ( 
                            new LibraryCardFieldDto()
                            {
                                Id = Guid.NewGuid(), // generate new id
                                Book = book, // student got this book
                                LibraryCard = libraryCard, // field is in this library card
                                IssueDate = issueDate, // note current date
                                ReturnDate = null // student still didn't return it
                            } )
                    ); 

                    unit.Save(); // save changes
                    return true;
                }
                else { return false; }    
            }
            else { return false; }    
        }

这里是我调用服务方法的代码,为清晰起见,这些代码应尽可能采用硬编码:

StudentService sts = new StudentService("Server=.\\SQLEXPRESS;Database=StudentsLibrary;Trusted_Connection=True;");

sts.TakeBookById(Guid.Parse("382234C3-721D-4F34-80E5-57657BBBBB32"), Guid.Parse("382C74C3-721D-4F34-80E5-57657B6CBC27"), DateTime.Now);

我还应用了从存储库中使用的方法:

public IEnumerable<T> FindAll()
{
     return this.LibraryContext.Set<T>();
}
public IEnumerable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
     return this.LibraryContext.Set<T>().Where(expression);
}

我没有在存储库中使用AsNoTracking(),因为我希望延迟加载能够正常工作(我不想为我的每个模型编写所有Inlude&ThenInclude东西)。

我想,如果专门跟踪这本书,那么我可以使上下文停止跟踪它。我写了这样的东西:

public void Reset()
{
     var entries = libraryContext.ChangeTracker.Entries().ToArray();
     foreach (var entry in entries)
     {
         entry.State = EntityState.Detached;
     }
}

并尝试了以下方法:

public void Reset()
{
     libraryContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
}

你知道吗?仍然存在该错误。我不知道是不是因为映射器?

更新:这是因为映射器。但是我该如何解决这种情况?如果在Create方法中创建DAL模型的对象,则该方法有效。我无法创建BLL模型的对象然后进行映射。 EF受不了这个东西。

请帮帮我。我是EF的新手。

1 个答案:

答案 0 :(得分:0)

拖放自动映射器。键入自己的扩展方法需要几秒钟,并且您可以轻松地对其进行完全控制。

您的主要问题是创建DTO然后将其转换为实体,但尚未正确配置。完全跳过该部分,仅创建实体。像这样使用DTO没有任何意义。在下面查看您的代码。

 unit.LibraryCardFields.Create( // create library card's field with current data
                        mapper.Map<LibraryCardField> ( 
                            new LibraryCardFieldDto()
                            {
                                Id = Guid.NewGuid(), // generate new id
                                Book = book, // student got this book
                                LibraryCard = libraryCard, // field is in this library card
                                IssueDate = issueDate, // note current date
                                ReturnDate = null // student still didn't return it
                            } )
                    ); 

对此进行更改,以创建自己的扩展方法并自己进行转换。

以这个为例。

假设您传递的是DTO,而不是创建它。

var LibraryCardField = LibraryCardFeildDto.ToEntity();//ToEntity is your extension method
 unit.LibraryCardFields.Create(LibraryCardField);

为扩展方法创建一个新类。

public static class ExampleMapperClass //or w/e you want to call it{

  public static LibaryCardField ToEntity(this LibraryCardFieldDto lbf){
      return new LibraryCardField{
        Id = lbf.Id,
        Book = lbf.book.ToEntity(),//create another extension method for this one
        LibraryCard  = LibraryCardDto.ToEntity(),//same here
        IssueDate = lbf.issueDate,
        ReturnDate = lbf.returnDate
      };
  }  
}