EF4 POCO WCF序列化问题(没有延迟加载,代理/无代理,循环引用等)

时间:2010-05-26 17:07:29

标签: wcf entity-framework poco entity-framework-4

好的,我想确保我的情况和我彻底尝试的一切。我很确定我能做什么,但我还没有找到成功的完美组合。

我正在使用 Entity Framework 4 RTM及其 POCO 支持。我正在寻找一个实体(Config),其中包含与另一个实体(App)的多对多关系。我为上下文转了 off lazy loading 禁用代理创建,并显式加载导航属性(通过.Include()或.LoadProperty())。但是,当加载导航属性(即,为给定的Config加载Apps)时,已加载的App对象已包含对已带入内存的Configs的引用。这会创建循环引用

现在我知道WCF使用的DataContractSerializer可以通过将 preserveObjectReferences 参数设置为true来处理循环引用。我已经尝试过这个我在网上发现的几个不同的属性实现。需要防止“对象图包含循环引用且无法序列化”错误。但是,它不会阻止整个图形的序列化,在Config和App之间来回传递。

如果我通过WcfTestClient.exe调用它,我从客户端得到一个stackoverflow(ha!)异常,我就被软管了。我从不同的调用环境得到不同的结果(C#单元测试与Web服务的本地引用似乎工作正常虽然我仍然可以无休止地在Configs和Apps之间来回钻取,但是从coldfusion环境调用它只返回第一个Config列表中的错误和其他错误。)我的主要目标是有一个我从EF明确加载的图表的序列化表示(即:Configs列表,每个都有他们的应用程序,但没有应用程序回到配置导航。)< / p>

注意:我也尝试过使用ProxyDataContractResolver技术并保持从我的上下文启用代理创建。这会引发抱怨所遇到的未知类型。我读到ProxyDataContractResolver在Beta2中没有完全工作,但应该在RTM中工作。

对于一些参考,这里大致是我如何查询服务中的数据:

var repo = BootStrapper.AppCtx["AppMeta.ConfigRepository"] as IRepository<Config>;
repo.DisableLazyLoading();
repo.DisableProxyCreation();

//var temp2 = repo.Include(cfg => cfg.Apps).Where(cfg => cfg.Environment.Equals(environment)).ToArray();
var temp2 = repo.FindAll(cfg => cfg.Environment.Equals(environment)).ToArray();
foreach (var cfg in temp2)
{
    repo.LoadProperty(cfg, c => c.Apps);
}

return temp2;

我认为问题的关键在于从实体框架4加载POCO对象的导航属性时,它预先填充了内存中已有对象的导航属性。尽管已尽力妥善处理循环引用,但这反过来又会影响WCF序列化。

我知道这是很多信息,但它确实阻碍了我们在系统中使用EF4 / POCO。我发现有几篇文章和博客涉及这些主题,但对于我的生活,我无法解决这个问题。随意简单地提问并帮助我集体讨论这种情况。

PS:为了彻底,我使用Spring.NET的HEAD版本注入WCF服务,以修复Spring.ServiceModel.Activation.ServiceHostFactory。但是我不认为这是问题的根源。

编辑:如果我没有循环引用,ProxyDataContractResolver类可以正常工作。 (即:我将App.Configs的setter设置为私有,这会阻止属性的序列化。)当它通过App对象命中Configs时,它会爆炸 - 它们似乎不会被识别为与顶级配置相同的类型。

EDIT2:看来EF或WCF无法识别实体确实相等。即:'Config'与特定的'Config.Apps [x] .Configs [y]'相同。实体键在每个模型的CSDL中正确设置,我已经重写了Equals()函数,以根据实体的“Id”属性来比较实体。这符合症状,因为没有抛出循环引用错误,但它确实是一个循环引用(并炸毁WcfTestClient.exe)并且当它到达'Config.Apps [x] .Configs [y]'级别时,ProxyDataContractResolver会爆炸配置。 (它不知道如何映射Config代理.ProxyDataContractResolver也可以工作。它就像它知道如何处理第一轮实体,但它认为是第二层不同的实体。)

哇,我可以罗嗦。对不起,伙计们!

5 个答案:

答案 0 :(得分:2)

您可能想查看我的blog post on this specific scenario - 如果无法解决您当前的困境,请给我发电子邮件!我也提供了一个示例解决方案。

请以任何方式给我一些反馈,我真的很想听到更多人关于这个特定问题 - 尤其是客户端的实施问题。

答案 1 :(得分:1)

答案 2 :(得分:1)

今天面对同样的问题并使用Value Injecter来解决它。它很简单:

var dynamicProxyMember = _repository.FindOne<Member>(m=>m.Id = 1);
var member = new Member().InjectFrom(dynamicProxyMember) as Member;

我们无力承担禁用ProxyCreation

的费用

答案 3 :(得分:0)

尝试设置myContext.ContextOptions.ProxyCreationEnabled = false;

如果问题得到解决(比如我的话),那么你就没有按照以下步骤进行操作:http://msdn.microsoft.com/en-us/library/ee705457.aspx

这解决了我的问题。

答案 4 :(得分:0)

您可以使用ApplyDataContractResolverAttributeProxyDataContractResolver以及CyclicReferencesAwareAttribute。起初这会产生类似这样的错误 - 好像根本没有指定DataContractResolver:

  

输入'System.Data.Entity.DynamicProxies.Whatever_E6 ...... A9',数据合约名称为'Whatever_E6 ...... A9:http://schemas.datacontract.org/2004/07/不期望System.Data.Entity.DynamicProxies'。考虑使用DataContractResolver或将任何静态未知的类型添加到已知类型列表中 - 例如,使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型列表中。

只需一个简单的改动即可。

在ApplyCyclicDataContractSerializerOperationBehavior中,DataContractSerializer的构造函数还必须传入DataContractResolver。这在我在网上看到的所有版本中都没有。

示例:

public class ApplyCyclicDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
    private readonly Int32 _maxItemsInObjectGraph;
    private readonly bool _ignoreExtensionDataObject;

    public ApplyCyclicDataContractSerializerOperationBehavior(OperationDescription operationDescription, Int32 maxItemsInObjectGraph, bool ignoreExtensionDataObject, bool preserveObjectReferences)
        : base(operationDescription)
    {
        _maxItemsInObjectGraph = maxItemsInObjectGraph;
        _ignoreExtensionDataObject = ignoreExtensionDataObject;
    }

    public override XmlObjectSerializer CreateSerializer(Type type, String name, String ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, knownTypes, 
            _maxItemsInObjectGraph, 
            _ignoreExtensionDataObject, 
            true, 
            null /*dataContractSurrogate*/, 
            DataContractResolver); // <-----------------------------
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, knownTypes, 
            _maxItemsInObjectGraph, 
            _ignoreExtensionDataObject, 
            true, 
            null /*dataContractSurrogate*/, 
            DataContractResolver); // <-----------------------------
    }
}