Domanda

I have an MVC3 project that is using an EF4 project as its Domain. The Domain is a Model first project that uses T4 to build POCO objects. There are several ComplexTypes at play in the Domain and everything works great as long as I use the proxies returned by context.CreateObject().

When an MVC3 action is called, the model binder passes a non-proxy object that contains the changes to be applied to the Domain.

I want to work with the "proxy'ed" original so the views have access to navigation properties later, so a straight up AttachTo doesn't cut it.

I need to get the "original" proxy'ed object from the context first, then update it with the changes contained in the POCO provided by the model binder.

From what I've read, and my research tells me, I should be able to accomplish this using something like the following:

public static T GetUpdatedProxy<T>(this ObjectContext context, string entitySetName, T entity)
    where T : class
{
    object original; // db original POCO, proxy wrapped.
    var entityKey = context.CreateEntityKey(entitySetName, entity);

    //Load DB object
    context.GetObjectByKey(entityKey, out original)
    //Apply changes from binder supplied POCO object.
    context.ApplyCurrentValues<T>(entitySetName, entity); //<= error here
    return (T) original;
}

My issue is this error:

[InvalidOperationException: The entity of type 'System.Data.Entity.DynamicProxies.Value_E954C24C522BA1D4124F434A57391656EFA4DD7CEFFD3A5CE35FC1532CD1B10A' references the same complex object of type 'Domain.DateRange' more than once. Complex objects cannot be referenced multiple times by the same entity.]
   System.Data.Objects.EntityEntry.CheckForDuplicateComplexObjects(Object complexObject) +418
   System.Data.Objects.EntityEntry.DetectChangesInProperties(Boolean detectOnlyComplexProperties) +211
   System.Data.Objects.Internal.EntityWithChangeTrackerStrategy.UpdateCurrentValueRecord(Object value, EntityEntry entry) +93
   System.Data.Objects.Internal.EntityWrapper`1.UpdateCurrentValueRecord(Object value, EntityEntry entry) +17
   System.Data.Objects.EntityEntry.ApplyCurrentValuesInternal(IEntityWrapper wrappedCurrentEntity) +107
   System.Data.Objects.ObjectContext.ApplyCurrentValues(String entitySetName, TEntity currentEntity) +365
  • The complex object on the non-proxy object and the one on the proxy'ed object are not the same.
  • The entity only has the one complex object so it can't be set multiple times to say, two properties of the same ComplexType.
  • The complex object itself hasn't actually had any values set to it so the two null-able fields are in fact still null.
  • If I do use the AttachTo method and then set the object state to modified the save works but I can't later use the object to return a view because the navigation properties are null.

Any thoughts? I appreciate the help.

È stato utile?

Soluzione

I finally figured out a workaround. I was absolutely unable to get context.ApplyCurrentValues() to work from a POCO to a Proxy.

The solution I came up with was to create a new proxy from the IOC CreateObject() method, use reflection to iterate over and copy the properties on the POCO, then call AttachTo() with the proxy.

public static T GetUpdatedProxy<T>(this ObjectContext context, string entitySetName, T entity)
    where T : class
{
    var proxy = context.CreateObject<T>();

    //Copy ComplexObjects and values over.
    foreach(var property in typeof(T).GetProperties().Where(p => p.CanWrite && p.CanRead))
        if(typeof(IComplexObject).IsAssignableFrom(property.PropertyType))
            proxy.SetValueOf(property, (entity.GetValueOf(property) as IComplexObject).Clone()); //<== Clone the ComplexType
        else if(typeof(System.ValueType).IsAssignableFrom(property.PropertyType) || typeof(System.String).IsAssignableFrom(property.PropertyType))
            proxy.SetValueOf(property, entity.GetValueOf(property));

    context.AttachTo(entitySetName, proxy);
    context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    entity = (T) proxy;
}

I tried to iterate and copy the properties to a preloaded proxy from context.TryGetObjectByKey(entityKey, out getObject) but it would give me a different error about the complex object being null (which it wasn't) when I tried to save the context.

I added a Clone-able interface and it's implementations to ComplexTypes in the t4 template:

public interface IComplexObject : ICloneable {}
...
object ICloneable.Clone(){ return this.Clone(); }
public DateRange Clone(){ return (DateRange) this.MemberwiseClone(); }

SetValueOf and GetValueOf are simple Extension Methods I use for readability:

public static object GetValueOf(this object item, PropertyInfo property)
{
    return property.GetValue(item, null);
}

public static object GetValueOf(this object item, string property)
{
    return GetValueOf(item, item.GetType().GetProperty(property));
}

public static void SetValueOf(this object item, PropertyInfo property, object value)
{
    property.SetValue(item, value, null);
}

public static void SetValueOf(this object item, string property, object value)
{
    SetValueOf(item, item.GetType().GetProperty(property), value);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top