This is a migrated thread and some comments may be shown as answers.

Weird WCF error working with OpenAccess objects

5 Answers 315 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Solomon
Top achievements
Rank 1
Solomon asked on 04 Aug 2010, 05:48 AM
I am getting a cryptic error with some simple functions in my WCF service. I have 2 functions that just get some persistent objects in a List. One of them works fine. The other (which has a many-to-many relationship) throws the following error when I try to invoke it using the WCF Test Client:

Failed to invoke the service. Possible causes: The service is offline or inaccessible; the client-side configuration does not match the proxy; the existing proxy is invalid. Refer to the stack trace for more detail. You can try to recover by starting a new proxy, restoring to default configuration, or refreshing the service.

This does not say alot and has nothing to do with refreshing, etc. Based on an issue with EntityFramework I found, I am led to believe that this is lazy loading related (remember that the function that does not work has m-to-m relationships). Perhaps an issue serializing collections?

I have tested this with several other functions. All of the ones with m-to-m relationships throw the error and ones without, work fine. Anyone ever see this error?

My WCF service implemenation
namespace IntegrationServices
{
    public class EventService : IServiceApi
    {
        public List<Event> GetAllEvents()
        {
            return new EventHelper().GetAllEvents();
        }
 
        public List<Speaker> GetAllSpeakers()
        {
            return new SpeakerHelper().GetAllSpeakers();
        }
    }
}

Helper biz object for Speaker
public class SpeakerHelper : BusinessServicesBase
{
    public List<Speaker> GetAllSpeakers()
    {
        var returnedSpeakers = from x in ObjectScope.Extent<Speaker>()
                       select x;
 
        return returnedSpeakers.ToList();
    }
}

Speaker persistent object (notice the ILists at the bottom)
[Telerik.OpenAccess.Persistent(IdentityField="speakerId")]
    public class Speaker
    {
        private int speakerId; // pk
 
        private string bioHTML;
 
        private string emailAddress;
 
        private int eventId;
 
        private string firstName;
 
        private string lastName;
 
        private IList<Document> document = new List<Document>();  // inverse Document.speaker
 
        private IList<Session> session = new List<Session>();  // inverse Session.speaker
 
        public Speaker()
        {
        }
...
}

5 Answers, 1 is accepted

Sort by
0
Alexander
Telerik team
answered on 06 Aug 2010, 01:02 PM
Hi Solomon,

In fact this is the expected behavior of the DataContractSerializer. The actual exception that you are probably getting is "Object graph for type XXX contains cycles and cannot be serialized if reference tracking is disabled." . You can find more details how to overcome the problem in this article. Hope that helps.

Greetings,
Alexander
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Solomon
Top achievements
Rank 1
answered on 10 Aug 2010, 06:22 AM
Alex,

I have never seen that error specifically and tried to implement the solution you references with no success. 

I do believe this has to do with my m-to-m relationships. When I mark an associative table as many-to-many in the reverse engineering wizard, I get the aformentioned error. When I leave these alone (not mark them as many-to-many), I get the following error in the WCFTestClient tool.

Is it best practice to mark these relationships as many-to-many? If I do, I can explicitly set the inverse field name. For example, if I have a session table, speaker table, and a session_speaker table (with two PKs mapped back to their respective owners). If I mark them as many-to-many, I can set "Session" as the inverse field and use the following code - Session.Speakers and Speaker.Sessions. If I do not mark them as many-to-many, I must use Session.Speakers and Speaker.SpeakerSessions. Is there a consequence for this. It seems logical that either would be able to be serialized for a WCF service. How does the Data Services Wizard handle this? This is getting frustrating. Please let me know if you would like to see some code.

Thanks,

Sol

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:GetAllSessionsResult. The InnerException message was 'There was an error deserializing the object of type DGI.MARS.Entities.Session[]. The maximum read depth (32) has been exceeded because XML data being read has more levels of nesting than is allowed by the quota. This quota may be increased by changing the MaxDepth property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 22342.'.  Please see InnerException for more details.

Server stack trace: 
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameter(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)
   at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters)
   at System.ServiceModel.Dispatcher.ProxyOperationRuntime.AfterReply(ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at IServiceApi.GetAllSessions()
   at ServiceApiClient.GetAllSessions()

Inner Exception:
There was an error deserializing the object of type DGI.MARS.Entities.Session[]. The maximum read depth (32) has been exceeded because XML data being read has more levels of nesting than is allowed by the quota. This quota may be increased by changing the MaxDepth property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 22342.
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.DataContractSerializer.ReadObject(XmlDictionaryReader reader, Boolean verifyObjectName)
   at System.ServiceModel.Dispatcher.DataContractSerializerOperationFormatter.DeserializeParameterPart(XmlDictionaryReader reader, PartInfo part, Boolean isRequest)

Inner Exception:
The maximum read depth (32) has been exceeded because XML data being read has more levels of nesting than is allowed by the quota. This quota may be increased by changing the MaxDepth property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 1, position 22342.
   at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)
   at System.Xml.XmlExceptionHelper.ThrowMaxDepthExceeded(XmlDictionaryReader reader, Int32 maxDepth)
   at System.Xml.XmlBaseReader.EnterScope()
   at System.Xml.XmlUTF8TextReader.ReadStartElement()
   at System.Xml.XmlUTF8TextReader.Read()
   at System.Runtime.Serialization.XmlReaderDelegator.Read()
   at System.Runtime.Serialization.CollectionDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
.....................................................
0
IT-Als
Top achievements
Rank 1
answered on 10 Aug 2010, 03:15 PM
Hi Solomon,

This was the error we were facing some 4+ years ago when we started using OA. You're on right track.

Since you're passing the persistent entities over the wire, they probably go on and on forever (during serialization as pointed out) because they point at each other (the m:n relationship)

This is just one of the problems that arises if you pass the persistent instances over the wire.

In general (at least I think so), it is not a good idea to pass your persistent entities over the wire. Here's why:

- Cyclic references (as you already learned)
- Unintentionally the serializer invokes the lazy loading of fields/collections not loaded upon serialization (you end up with a huge graph of objects)
- Worst of all (I think) you expose the "inner-workings" of your persistence model. In my opinion, how your object storage is organized does not need to be exposed to the "world outside" of your WCF services.

Our solution (and I know it means more complex system) is to make a DataContract layer of the classes that are actually used to perform the requested service method, say DataContract.Order GetOrder(int orderId) or SaveOrder(DataContract.Order order)

I know you have to map between two models... the one you expose to the outside in your services and the one you keep close yourself - the persistence model.

Since our system is a multi-tenant system we also had some security matters, that also pointing in the direction of making "another model" as opposed to one model for both the external (consumers of WCF services) and internal (persistence model). Some type of "tenant" may not see all information stored in the persistence model, only part of it...for example.
If you send your persistent classes over the wire *everything* is exposed... Down the object graph..

It is a matter of choosing between a) the DSW way of doing it (and it may be perfect for your needs), where you essentially expose your persistent mode or b) "shaping" your persistent model into DataContract classes that your services expose.

Regards

Henrik



0
Charlie
Top achievements
Rank 1
answered on 19 Oct 2010, 05:51 PM
Just a thought  - but how about using a DataContainer (includes ORM persistence objects) library?  Such library will be shared to the client and WCF.
Only at the WCF level will there be a datasource.

Thoughts?



0
IT-Als
Top achievements
Rank 1
answered on 19 Oct 2010, 06:02 PM
Hi Charlie,

Actually, there is a "Container" approach for using with OpenAccess built in. Depending on the usage scenario, there are some drawbacks:

- The whole point when doing "services" is to have a clean interface, meaning nice endpoints and well-defined message and data contracts for interacting with the service. This kind of "get lost" when packing into a container.
- Interop with other platforms. The neat thing about services is that it CAN be interoperable with a Java client (for example) if you adhere to standards...  This, too, get lost when using a container approach.

In my opinion a service should be so well defined in terms of contracts that by looking a the bare method signature a consumer knows how to interact with it.

These a my thoughts..

Tags
General Discussions
Asked by
Solomon
Top achievements
Rank 1
Answers by
Alexander
Telerik team
Solomon
Top achievements
Rank 1
IT-Als
Top achievements
Rank 1
Charlie
Top achievements
Rank 1
Share this question
or