0

我有一个使用 EF4 项目作为其域的 MVC3 项目。Domain 是一个模型优先项目,它使用 T4 来构建 POCO 对象。Domain 中有几个 ComplexTypes 在起作用,只要我使用 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 的两个属性。
  • 复杂对象本身实际上并没有设置任何值,因此这两个可以为空的字段实际上仍然为空。
  • 如果我确实使用了 AttachTo 方法,然后将对象状态设置为修改保存工作,但我以后不能使用该对象返回视图,因为导航属性为空。

有什么想法吗?我很感激帮助。

4

1 回答 1

0

我终于想出了一个解决方法。我绝对无法让 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) 迭代并将属性复制到预加载的代理,但是当我尝试保存上下文时,它会给我一个关于复杂对象为空(它不是)的不同错误.

我在 t4 模板中添加了一个 Clone-able 接口及其对 ComplexTypes 的实现:

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);
}
于 2011-10-08T16:13:10.213 回答