views:

1358

answers:

5

This code fails to actually save any changes:

//
// POST: /SomeType/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, SomeType Model)
{
    db.AttachTo(Model.GetType().Name, Model);
    db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);
    db.SaveChanges();
    return RedirectToAction("Index");
}

ASP.NET MVC creates the object Model as a Department type EntityObject with an EntityState value of Detached.

After using the AttachTo method, its EntityState becomes Unchanged.

MSDN on Attaching Objects (Entity Framework)

Objects are attached to the object context in an Unchanged state.

Because of its Unchanged state, the method ApplyPropertyChanges does nothing.

I want it to instead have state Modified.

MSDN on EntityState Enumeration

Detached
The object exists but it is not being tracked by Object Services. An entity is in this state immediately after it has been created and before it is added to the object context. An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded using a NoTrackingMergeOption.

Unchanged
The object has not been modified since it was loaded into the context or since the last time that the SaveChanges method was called.

Modified
The object is changed but the SaveChanges method has not been called.

I cannot explicitly set an EntityObject's EntityState property to Modified. It is read only.

Is it just impossible to have strongly-typed MVC controllers with EntityObjects?

+1  A: 

This works:

//
// POST: /SomeType/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, SomeType Model)
{
    Model.EntityKey = (from SomeType s in db.SomeType
                       where s.Id == id
                       select s).FirstOrDefault().EntityKey;
    db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);
    db.SaveChanges();
    return RedirectToAction("Index");
}

But is there a way without querying the database?

Zack Peterson
+1  A: 

What happens if you add one line:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, SomeType Model)
{
    db.AttachTo(Model.GetType().Name, Model);
    Model.SomeProperty = Model.SomeProperty; // This looks hacky... =(
    db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);
    db.SaveChanges();
    return RedirectToAction("Index");
}

Does the state change?

Tomas Lycken
I tried Model.Id = Model.Id but that caused an exception because that particular field is protected by the Entity Framework. I didn't try any others.
Zack Peterson
Yeah, the ID is readonly. Try any other... =)
Tomas Lycken
+5  A: 

You need to get the ObjectStateManager from your ObjectContext. With the ObjectStateManager, you can explicitly set the state for your object without needing to make a call to the database:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, SomeType Model)
{
    db.AttachTo(Model.GetType().Name, Model);

    ObjectStateManager stateMgr = db.ObjectStateManager;
    ObjectStateEntry stateEntry = stateMgr.GetObjectStateEntry(model);
    stateEntry.SetModified(); // Make sure the entity is marked as modified
    //db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model);

    db.SaveChanges();
    return RedirectToAction("Index");
}

The ObjectStateEntry also allows you to apply finer-grained state change data via the SetModifiedProperty. If you call SetModified, EF will treat the entire entity as modified, and persist every property to the data store. With SetModifiedProperty, EF can optimize the queries and only involve the properties that have actually changed. Using SetModifiedProperty is obviously more complex, as you usually need to know the original value of each property.

I hope this helps. ObjectStateManager is a powerful little tool in the EF toolbox, and can help improve EF v1.0's otherwise morbid performance and efficiency.

jrista
SetModifiedProperty works as expected. But, SetModified doesn't seem to treat the entire entity as modified. Rather than "persist every property to the data store", it persists none.
Zack Peterson
Thats a bit odd...there may be something else that needs to be done...but that sounds like a bug somewhere.
jrista
+1  A: 

Hi,

I found none of the above seemed to work for me, but the MSDN guide example did which is where you get the original item from the context, and then attach the updated item.

Second example:

http://msdn.microsoft.com/en-us/library/bb896248.aspx#Mtps_DropDownFilterText

private static void ApplyItemUpdates(SalesOrderDetail updatedItem){
// Define an ObjectStateEntry and EntityKey for the current object.
EntityKey key;
object originalItem;

using (AdventureWorksEntities advWorksContext =
    new AdventureWorksEntities())
{
    try
    {
        // Create the detached object's entity key.
        key = advWorksContext.CreateEntityKey("SalesOrderDetail", updatedItem);

        // Get the original item based on the entity key from the context
        // or from the database.
        if (advWorksContext.TryGetObjectByKey(key, out originalItem))
        {
            // Call the ApplyPropertyChanges method to apply changes
            // from the updated item to the original version.
            advWorksContext.ApplyPropertyChanges(
                key.EntitySetName, updatedItem);
        }

        advWorksContext.SaveChanges();
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

}

Sky
+1  A: 

Hi, I have this working, but what iam not able to get at is the parent entity of SalesOrderDetail How can i get at it?

SalesOrderDetail.SalesOrderHead is null and also entityreference is null. My edit routine has access to the id of SalesOrderDetail only.

Thanks Sri

sri
Try the "Ask Question" link to start a new Question. Your issue will get more attention.
Zack Peterson