views:

1356

answers:

2

How can I use strongly-typed Controllers with EntityObjects?

My failures...

First I tried this:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
    db.SaveChanges();
    return RedirectToAction("Index");
}

This failed to actually save any changes to the database. So, I tried to attach the model to my ObjectContext:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
    db.Attach(Model);
    db.SaveChanges();
    return RedirectToAction("Index");
}

This failed because "an object with a null EntityKey value cannot be attached to an object context." So, I tried to assign the EntityKey:

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

This failed because "an object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."

How is this supposed to work?

+1  A: 

Try db.AttachTo() instead of db.Attach()

As described here

"Call AttachTo on ObjectContext to attach the object to a specific entity set in the object context. Also do this if the object has a null (Nothing in Visual Basic) EntityKey value."

Edit: actually, you may need to call ApplyPropertyChanges() in this case.

Edit 2: You can build an entity key for your object with code like the following:

public EntityKey GetEntityKey(string entitySetName, object keyValue)
{
  IEnumerable<KeyValuePair<string, object>> entityKeyValues =
    new[]
    {
      new KeyValuePair<string, object>("Id", keyValue)
    };

  var key = new EntityKey(string.Concat("YOUR_OBJECT_CONTEXT_NAME.", entitySetName), entityKeyValues);
  return key;
}

And then you'd call this with something like this for entity keys based on a single database column called 'Id':

var entityKey = GetEntityKey("Customer", 23);

I use a similar method in my own EF code except with a Generic type argument to avoid having to use a string for the entity class name.

Steve Willcock
This works: Model.EntityKey = ...; db.ApplyPropertyChanges("Department", Model); db.SaveChanges();
Zack Peterson
But, is there any way to not have to perform a database query to determine the EntityKey?
Zack Peterson
http://stackoverflow.com/questions/898260/what-value-for-qualifiedentitysetname-do-i-need-for-an-entitykey-constructor
Zack Peterson
Just answered this here in the edits
Steve Willcock
I've updated the other question: http://stackoverflow.com/questions/898260/how-do-i-construct-an-entitykey
Zack Peterson
+3  A: 

The thing is, what you have as your input is not a complete model object with changed properites - it is just a "start" of new one, with all the properties that you want to overwrite on an already existing entity. You don't post any of the ID properties,

This looks a little verbose, but it's the best I've found, so it is how I do it in my projects (ajusted to your class names, with whatever was missing made up...):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
    var dbDepartment = (from Department d in db.Department
                        where d.Id == id
                        select d).FirstOrDefault() as Department

    dbDepartment.Name = Model.Name;
    dbDepartment.Color = Model.Color;
    // etc, assigning values manually...

    try
    {
        db.SaveChanges();
    }
    catch (Exception ex)
    {
        // oops...
    }
    return RedirectToAction("Index");
}
Tomas Lycken
That doesn't really look like a step forward ;).
Morph
Not if what matters is beautiful code. This isn't beautiful... But it works, so compared with code that doesn't, I must say it is a step forward! ;)
Tomas Lycken