I need to set an EntityObject\'s EntityKey. I know its type and its id value. I don\'t want to query the database unnecessarily.
This works...
//
//
I had the same problem, while it was working on other pages. I dont know if this comment will be useful... But I finally discovered, that in my objet (the equivalent of your Departement), my "Id" was not appearing in the form...
Just the fact to add it (while I had removed it...) on my form has resolved my problem. (with the style="visibility:hidden", in order not to see him...)
public static class EfExtensions
{
public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity, Func<T, int> getIdDelegate)
where T : EntityObject
{
var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name;
T newEntity = Activator.CreateInstance<T>();
newEntity.EntityKey = db.CreateEntityKey(entitySetName, entity);
Type t = typeof(T);
foreach(EntityKeyMember keyMember in newEntity.EntityKey.EntityKeyValues) {
PropertyInfo p = t.GetProperty(keyMember.Key);
p.SetValue(newEntity, keyMember.Value, null);
}
db.Attach(newEntity);
//db.AcceptAllChanges();
db.ApplyPropertyChanges(entitySetName, entity);
}
}
Improving upon Steve Willcock's great implementation, here's my suggestion.
It uses Reflection (a part of .NET) way more than the original example, to save you some code. It also automatically supports any type of entity class, and not just a "Department".
Furthermore, it gets rid of the obsolete ApplyPropertyChanges
method, and uses the new ApplyCurrentValues
method.
The method
The method basically just uses reflection to get the value of the "Id" property dynamically, and setting it too. This saves all the hassle with a delegate.
public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity) where T : EntityObject
{
PropertyInfo idProperty = typeof(T).GetProperty("Id");
var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name;
var id = idProperty.GetValue(entity, null);
var entityKey = new EntityKey(entitySetName, "Id", id);
Type type = entity.GetType();
EntityObject obj = (EntityObject)Activator.CreateInstance(type);
idProperty.SetValue(obj, id, null);
obj.EntityKey = entityKey;
db.Attach(obj);
db.AcceptAllChanges();
db.ApplyCurrentValues(entitySetName, entity);
}
Usage
Using it is pretty simple as well.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
db.ApplyDetachedPropertyChanges(Model);
db.SaveChanges();
}
The reason your second block of code fails is because EF can't find the object in the ObjectStateManager - i.e. when it pulls objects from the db it puts them in the state manager so it can track them - this is similar to the Identity Map pattern. Despite having an EntityKey, your object isn't in the state manager so EF is unable to persist the changes. You can get around this by putting the object into the state manager youself but you have be a bit sneaky about it.
This works:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department model)
{
var entitySetName = db.DefaultContainerName + "." + model.GetType().Name;
var entityKey = new System.Data.EntityKey(entitySetName, "Id", model.Id);
db.Attach(new Department{Id = id, EntityKey = entityKey});
db.AcceptAllChanges();
db.ApplyPropertyChanges(entitySetName, model);
db.SaveChanges();
}
... but it's not very clean. Basically this is attaching an 'empty' object with just an entity key, accepting all changes and then calling ApplyPropertyChanges with the actual real updated values.
Here's the same thing wrapped up in an extension method - this should work for anything that has uses a single db column for the primary key. The only interesting part of calling the method is that you need to tell it how to find the key property via a delegate as the second argument to the extension method:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department model)
{
db.ApplyDetachedPropertyChanges(model, x => x.Id);
db.SaveChanges();
}
and the extension method:
public static class EfExtensions
{
public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity, Func<T, int> getIdDelegate)
where T : EntityObject
{
var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name;
var id = getIdDelegate(entity);
var entityKey = new EntityKey(entitySetName, "Id", id);
db.Attach(new Department {Id = id, EntityKey = entityKey});
db.AcceptAllChanges();
db.ApplyPropertyChanges(entitySetName, entity);
}
}
As the extension method is calling AcceptAllChanges, you'd need to be careful about calling this if you are doing updates on multiple entities at once - you could easily 'lose' updates if you aren't careful. Hence this approach is only really suitable for simple update scenarios - e.g. a lot of MVC action methods :)
Try to use below code and let me know if it is working for you.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Guid id, Department Model)
{
using (var context = new EntityContext())
{
try
{
Object entity = null;
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>("DepartmentID", id) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey("EntityContext.Deparment",
entityKeyValues);
// Get the object from the context or the persisted store by its key.
if (context.TryGetObjectByKey(key, out entity))
{
context.ApplyPropertyChanges(key.EntitySetName, Model);
context.SaveChanges();
}
else
{
// log message if we need
//"An object with this key could not be found."
}
}
catch (EntitySqlException ex)
{
// log message
}
}
}