After setting EntityState.Unchanged the entity pro

2019-06-09 11:23发布

问题:

I have an ASP.Net MVC project where I add in a View a student entity to my Entity Framework 6 repository.

class Student
  public long Id { get; set; }
  public virtual Book Favorite { get; set; }

class Book
  public long Id { get; set; }
  public string Title { get; set; }

The Favorite property is set from a drop down in the view. In the controller on post, only the Id for the book is set. To tell Entity Framework, to only add the student, but not to add the referenced book, I set the EntityState for the Book to Unchanged as described here

After saving my changes, I have the correct student record in the database and also the book record remains unchanged, but whenever I query now the book from my repository (not via the student), I get a book entity back where only the Id is set and Title and all other properties are null.

My Controller (simplified):

public ActionResult Create()
{
    StudentViewModel result = new StudentViewModel();
    result.BookList = new SelectList(DbContext.Books, "Id", "Name");
    return View(result);
}

[HttpPost]
public ActionResult Create(StudentViewModel viewModel)
{
    DbContext.Entry(viewModel.NewStudent.Favorite).State = EntityState.UnChanged);
    DbContext.Add(viewModel.NewStudent);
    DbContext.SaveChanges();

    Book missingProperties = 
        DbContext.Books.Single(book => book.Id == viewModel.NewStudent.Favorite.Id);
} 

My View (simplified):

@using System.Linq
@model StudentViewModel
@using (Html.BeginForm())
{        
    ...
    @Html.TextBoxFor(model => model.NewStudent.Name)
    @Html.DropDownListFor(model => model.NewStudent.Favorite.Id, Model.BookList)
    ...
}

回答1:

You should query for Favorite the db, before saving new student(and check if there is one there, cause you should not anything what comes from client=), that would get the book to the context, so you just set it to the student:

Book book = DbContext.Books.Single(book => book.Id == viewModel.NewStudent.Favorite.Id);
if(book!=null)
{
    viewModel.NewStudent.Favorite = book;
}
else
{
    throw new Exception();
}
DbContext.Add(viewModel.NewStudent);
DbContext.SaveChanges();


回答2:

The problem is, that the Entity Framwork DbContext caches entities.

With lazy loading, you simply need to make some reference to the related data and the Entity Framework will check to see whether it’s been loaded into memory. If not, the Entity Framework will create and execute a query behind the scenes, populating the related data.

By default, Visual Studio will define newly created models to set LazyLoadingEnabled to true.

(from MSDN)

Once loaded from db, it will not be loaded again for the DbContext lifetime. EntityState.UnChanged prevents Entity Framework only from storing the entity back to the database. But whenever the entity is retrieved from context a second time, not the correct value from database is taken, but the modified, that is now also in cache.

It is additionally necessary to refresh the modified entity. Attach it in the DbContext and then reload it.

Book result = Attach(book);
Entry(book).Reload();

I think, this answers the issue, but I am not sure if GuruStron answer uses the better pattern.