Применение текущих значений из POCO к DynamicProxy дает ошибку для сложных объектов

У меня есть проект MVC3, который использует проект EF4 в качестве домена. Домен — это первый проект модели, который использует T4 для создания объектов POCO. В домене задействовано несколько сложных типов, и все отлично работает, пока я использую прокси-серверы, возвращаемые context.CreateObject().

Когда вызывается действие MVC3, связыватель модели передает объект без прокси, который содержит изменения, которые должны быть применены к домену.

Я хочу работать с "прокси"-оригиналом, чтобы представления имели доступ к свойствам навигации позже, поэтому прямое приложение AttachTo не сокращает его.

Мне нужно сначала получить «исходный» прокси-объект из контекста, а затем обновить его с помощью изменений, содержащихся в POCO, предоставленном связывателем модели.

Из того, что я читал, и мои исследования говорят мне, я должен быть в состоянии выполнить это, используя что-то вроде следующего:

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;
}

Моя проблема заключается в этой ошибке:

[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
  • Сложный объект на непрокси-объекте и объект на прокси-объекте не совпадают.
  • Объект имеет только один сложный объект, поэтому его нельзя установить несколько раз, чтобы, скажем, два свойства одного и того же типа ComplexType.
  • Для самого сложного объекта на самом деле не было задано никаких значений, поэтому два поля, допускающие значение NULL, на самом деле все еще равны NULL.
  • Если я использую метод AttachTo, а затем устанавливаю состояние объекта для изменения, сохранение работает, но я не могу позже использовать объект для возврата представления, потому что свойства навигации имеют значение null.

Есть предположения? Я ценю помощь.


person Shaun Kohanowski    schedule 05.10.2011    source источник


Ответы (1)


Наконец-то я нашел обходной путь. Мне абсолютно не удалось заставить context.ApplyCurrentValues() работать с POCO на прокси.

Решение, которое я придумал, состояло в том, чтобы создать новый прокси из метода IOC CreateObject(), использовать отражение для повторения и копирования свойств в POCO, а затем вызвать AttachTo() с прокси.

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;
}

Я попытался выполнить итерацию и скопировать свойства в предварительно загруженный прокси-сервер из context.TryGetObjectByKey(entityKey, out getObject), но это дало бы мне другую ошибку о том, что сложный объект является нулевым (чего не было), когда я пытался сохранить контекст .

Я добавил интерфейс с возможностью клонирования и его реализации в ComplexTypes в шаблоне t4:

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

SetValueOf и GetValueOf — это простые методы расширения, которые я использую для удобства чтения:

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);
}
person Shaun Kohanowski    schedule 08.10.2011