开发者

EF4 POCO WCF Serialization problems (no lazy loading, proxy/no proxy, circular references, etc)

OK, I want to make sure I cover my situation and everything I've tried thoroughly. I'm pretty sure what I need/want can be done, but I haven't quite found the perfect combination for success.

I'm utilizing Entity Framework 4 RTM and its POCO support. I'm looking to query for an entity (Config) that contains a many-to-many relationship with another entity (App). I turn off lazy loading and disable proxy creation for the context and explicitly load the navigation property (either through .Include() or .LoadProperty()). However, when the navigation property is loaded (that is, Apps is loaded for a given Config), the App objects that were loaded already contain references to the Configs that have been brought to memory. This creates a circular reference.

Now I know the DataContractSerializer that WCF uses can handle circular references, by setting the preserveObjectReferences parameter to true. I've tried this with a couple of different attribute implementations I've found online. It is needed to prevent the "the object graph contains circular references and cannot be serialized" error. However, it doesn't prevent the serialization of the entire graph, back and forth between Config and App.

If I invoke it via WcfTestClient.exe, I get a stackoverflow (ha!) exception from the client and I'm hosed. I get different results from different invocation environments (C# unit test with a local reference to the web service appears to work ok though I still can drill back and forth between Configs and Apps endlessly, but calling it from a coldfusion environment only returns the first Config in the list and errors out on the others.) My main goal is to have a serialized representation of the graph I explicitly load from EF (ie: list of Configs, each with their Apps, but no App back to Config navigation.)

NOTE: I've also tried using the ProxyDataContractResolver technique and keeping the proxy creation enabled from my context. This blows up complaining about unknown types encountered. I read that the ProxyDataContractResolver didn't fully work in Beta2, but should work in RTM.

For some reference, here is roughly how I'm querying the data in the service:

var repo = BootStrapper.AppCtx["AppMeta.ConfigRepository"] as IRepository<Config>;
repo.DisableLazyLoading();
repo.Disable开发者_如何学JAVAProxyCreation();

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

I think the crux of my problem is when loading up navigation properties for POCO objects from Entity Framework 4, it prepopulates navigation properties for objects already in memory. This in turn hoses up the WCF serialization, despite every effort made to properly handle circular references.

I know it's a lot of information, but it's really standing in my way of going forward with EF4/POCO in our system. I've found several articles and blogs touching upon these subjects, but for the life of me, I cannot resolve this issue. Feel free to simply ask questions and help me brainstorm this situation.

PS: For the sake of being thorough, I am injecting the WCF services using the HEAD build of Spring.NET for the fix to Spring.ServiceModel.Activation.ServiceHostFactory. However I don't think this is the source of the problem.

EDIT: The ProxyDataContractResolver class works correctly if I don't have the circular references. (i.e.: I make the setter of App.Configs to be private, which prevents serialization of the property.) It blows up, it appears, when it hits Configs via the App object -- they don't seem to be recognized as the same type as the top level Configs.

EDIT2: It appears that either EF or WCF doesn't recognize that the entities are indeed equal. i.e.: 'Config' is the same as a particular 'Config.Apps[x].Configs[y]'. The entity keys are properly set in the CSDL for each model and I've overridden the Equals() function to compare entities based on their 'Id' property. This fits the symptoms as no circular reference error is thrown, yet it is indeed a circular reference (and blows up WcfTestClient.exe) AND the ProxyDataContractResolver blows up when it hits the 'Config.Apps[x].Configs[y]' level of Configs. (It doesn't know how to map a Config proxy. The ProxyDataContractResolver works otherwise. It's like it knows how to handle the initial round of entities, but the second level it considers as different entities.)

Wow, I can be wordy. Sorry folks!


You might want to check out my blog post on this specific scenario - please email me if it doesn't help fix your current predicament! I've included a sample solution as well.

Please drop me some feedback either way, I'd really like to hear from more people on this particular issue - especially with the implementation problems on the client end of things.


hrmm, I may not have fully understood the issue, but everytime I run into circular references with WCF the answer is to change [DataContract] on the offending classes to [DataContract(IsReference = true)].

This is a huge help compared to all the drek of messing with the contract resolvers that was needed pre WCF 3.5 SP1.

Hope this helps.


Faced the same issue today and used Value Injecter to solve it. It's as simple as:

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

We couldnt afford disabling ProxyCreation


Try setting myContext.ContextOptions.ProxyCreationEnabled = false;

If the problem is solved (like mine) then you've not followed the steps mentioned in: http://msdn.microsoft.com/en-us/library/ee705457.aspx

This solved the problem for me.


You can use the ApplyDataContractResolverAttribute and a ProxyDataContractResolver along with the CyclicReferencesAwareAttribute. At first this produces error like this one - as if there is no DataContractResolver specified at all:

Type 'System.Data.Entity.DynamicProxies.Whatever_E6......A9' with data contract name 'Whatever_E6......A9:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

It will work with one simple change.

In the ApplyCyclicDataContractSerializerOperationBehavior, the constructors for the DataContractSerializer must also pass in the DataContractResolver. This is left out of all the versions I have seen online.

Example:

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); // <-----------------------------
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜