Entity-Framework to Update Exception?

.net-4.0 asp.net-mvc c# entity-framework

Question

Hi,

I am using EntityFramework for my ASP.NET MVC website but have some problems with the update.

This is how my update code looka like :

using (BissEntities context = new BissEntities())
    {

      if (adCategoryFilter.Id < 1)
        context.AddToAdCategoryFilter(adCategoryFilter);
      else
        context.Refresh(System.Data.Objects.RefreshMode.ClientWins, adCategoryFilter);

      if (context.SaveChanges() > 0)
        return true;
    }
    return false;

When executing the context.Refresh i get the following exception :

The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.

Stacktrace :    at System.Data.Objects.ObjectContext.RefreshCheck(Dictionary`2 entities, Object entity, EntityKey key)
   at System.Data.Objects.ObjectContext.AddRefreshKey(Object entityLike, Dictionary`2 entities, Dictionary`2 currentKeys)
   at System.Data.Objects.ObjectContext.RefreshEntities(RefreshMode refreshMode, IEnumerable collection)
   at System.Data.Objects.ObjectContext.Refresh(RefreshMode refreshMode, Object entity)
   at Biss.Models.FilterModel.UpdateCategoryFilter(AdCategoryFilter adCategoryFilter) in C:\Users\Snowman\Documents\Visual Studio 2010\Projects\Biss\Biss\Models\FilterModel.cs:line 86 

This is not the first time I get this problem. First I thought that it might have to do with the relations in the database but after these was removed from the effected table the same exception remained.

Where does the adCategoryFilter come from?

The adCategoryFilter is instansiated(new) and then filled with data from the ViewObject(from the website). It does have the required data like filter Id (to map the filter to correct row in db).

Pleas explain why Im getting this problem and how I could solve it.

BestRegards

1
3
12/13/2010 3:18:45 PM

Accepted Answer

Because your using ASP.NET MVC, your working in a stateless environment. That means, once a request has finished processing, there is no more "Entity Framework Memory", or "The Graph".

So, you need to explicitly tell EF you wish to add or update.

Here's how you do it:

using (BissEntities context = new BissEntities())
{
  if (adCategoryFilter.Id < 1)
    context.AdCategoryFilters.AddObject(adCategoryFilter);
  else {
     var stub = new AdCategoryFilters { Id = adCategoryFilter.Id };
     context.AdCategoryFilters.Attach(stub);
     context.AdCategoryFilters.ApplyCurrentValues(adCategoryFilter);
  }

  context.SaveChanges();
}

That is referred to as the stub technique.

In short, you create a new entity with the same entity key as the entity you are trying to UPDATE (in your case, the entity key is "Id").

You then "attach" this stub (so it's tracked by the EF internal graph), then override the values on this stub with your entity to UPDATE, then save changes.

I can't use UpdateModel, as i have a multi-layered architecture and use POCO's, custom viewmodels, etc - so i have created a custom "UpdateModel" method on my service/repository - which does a (more complicated) version of the above.

Also try not to use "if Id < 1, it's an add" with ASP.NET MVC - as if you forget to bind the ID on the view, it will be passed as 0, so even though you could be doing an update, your above code will try and do an add.

Instead be more explicit - have seperate action methods for Add/Update.

HTH.

5
12/13/2010 11:32:04 PM

Popular Answer

Instead of refreshing, try retrieving the object and updating its properties using something like an auto-mapper (or UpdateModel in MVC controller)

The EntityKey is a separate thing to the id property, with some other stuff going on under the hood. Your newly created object is missing this stuff, which is where the problem is coming from.

The pattern goes a little like (not a C# guy so please excuse syntax):

var context = new MyEntities();
var originalObject = context.MyObjectSet.Single(x => x.Id == viewmodel.Id);
UpdateModel(originalObject);
context.SaveChanges();

The crucial difference is that the newly retrieved object has got the EntityKey all set correctly. You can validly use the id property to detect a new/existing object, but there is more to the EntityKey than just that property.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow