将 POCO 中的当前值应用到 DynamicProxy 会在复杂对象上出现错误

发布于 2024-12-08 12:44:06 字数 2103 浏览 0 评论 0原文

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

流年已逝 2024-12-15 12:44:06

我终于想出了一个解决方法。
我绝对无法让 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 模板中添加了一个可克隆的接口及其对 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);
}

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文