Problem sending Dataset with an 'AnyType' field.
Hi,
I am currently trying to send a dataset from my client application to my service. With a 'normal' dataset where every field is of a certain well defined type, this doesn't cause any problems.
When one of the fields of the dataset is a field of type 'anytype' however, things go wrong...
The dataset-schema:
<?
xml version="1.0" standalone="yes"?><
xs:schema id="QueriesDataset" targetNamespace="http://tempuri.org/QueryDataset.xsd" xmlns:mstns="http://tempuri.org/QueryDataset.xsd" xmlns="http://tempuri.org/QueryDataset.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified"><
xs:element name="QueriesDataset" msdata:IsDataSet="true" msdata:Locale="nl-BE"><
xs:complexType><
xs:choice minOccurs="0" maxOccurs="unbounded"><
xs:element name="TestTable"><
xs:complexType><
xs:sequence><
xs:element name="ID" type="xs:int" /><
xs:element name="TestField1" type="xs:string" minOccurs="0" /><
xs:element name="TestField2" type="xs:double" minOccurs="0" /><
xs:element name="TestField3" type="xs:boolean" minOccurs="0" /><
xs:element name="TestField4" msdata:DataType="System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:anyType" minOccurs="0" /></
xs:sequence></
xs:complexType></
xs:element></
xs:choice></
xs:complexType></
xs:element></
xs:schema>At clientside I create some rows for this dataset and then I try to pass this to the service. In the anytype field I inserted an integer.
This results in the following error (at serverside : looked at it using the Trace Viewer):
System.Data.DataException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Undefined data type: 'xs:int'.If I go look at the message the service received from the client, I can see that everything is there like it is supposed to be. It just cannot be deserialized because of the anytype field that in this case is an integer. (When I change this integer into a string , I get the same error with this difference that the undefined data type is 'xs:string'.)
Message received from the client (at service:using Trace Viewer):
<
E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent"><
System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system"><
EventID>0</EventID><
Type>3</Type><
SubType Name="Information">0</SubType><
Level>8</Level><
TimeCreated SystemTime="2007-01-23T08:17:02.3714689Z" /><
Source Name="System.ServiceModel.MessageLogging" /><
Correlation ActivityID="{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}" /><
Execution ProcessName="ServiceHost.vshost" ProcessID="9999" ThreadID="99" /><
Channel /><
Computer>XXXXXXXX</Computer></
System><
ApplicationData><
TraceData><
DataItem><
MessageLogTraceRecord Time="2007-01-23T09:17:02.3714689+01:00" Source="Malformed" Type="System.ServiceModel.Channels.MessagePatterns+PatternMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace"><
s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope"><
s:Header><
a:Action s:mustUnderstand="1">http://tempuri.org/IService1/MyOperation2</a:Action><
a:MessageID>urn:uuid:4a32680a-67a7-4c79-9004-9b1dcd41c889</a:MessageID><
a:ReplyTo><
a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></
a:ReplyTo><
a:To s:mustUnderstand="1">net.tcp://localhost/WCFTryOutsService/WCFTryOutsHost</a:To></
s:Header><
s:Body><
MyOperation2 xmlns="http://tempuri.org/"><
myDS><
xs:schema id="TestDataset" targetNamespace="http://tempuri.org/QueryDataset.xsd" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mstns="http://tempuri.org/QueryDataset.xsd" xmlns="http://tempuri.org/QueryDataset.xsd" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><
xs:element name="TestDataset" msdata:IsDataSet="true" msdata:Locale="nl-BE"><
xs:complexType><
xs:choice minOccurs="0" maxOccurs="unbounded"><
xs:element name="TestTable"><
xs:complexType><
xs:sequence><
xs:element name="ID" type="xs:int"></xs:element><
xs:element name="TestField1" type="xs:string" minOccurs="0"></xs:element><
xs:element name="TestField2" type="xs:double" minOccurs="0"></xs:element><
xs:element name="TestField3" type="xs:boolean" minOccurs="0"></xs:element><
xs:element name="TestField4" type="xs:anyType" minOccurs="0"></xs:element></
xs:sequence></
xs:complexType></
xs:element></
xs:choice></
xs:complexType></
xs:element></
xs:schema><
diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"><
QueriesDataset xmlns="http://tempuri.org/QueryDataset.xsd"><
TestTable diffgr:id="TestTable1" msdata:rowOrder="0" diffgr:hasChanges="inserted"><
ID>1</ID><
TestField1>False</TestField1><
TestField2>0</TestField2><
TestField3>false</TestField3><
TestField4 xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">0</TestField4></
TestTable><
TestTable diffgr:id="TestTable2" msdata:rowOrder="1" diffgr:hasChanges="inserted"><
ID>2</ID><
TestField1>True</TestField1><
TestField2>1</TestField2><
TestField3>true</TestField3><
TestField4 xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1</TestField4></
TestTable></
QueriesDataset></
diffgr:diffgram></
myDS></
MyOperation2></
s:Body></
s:Envelope></
MessageLogTraceRecord></
DataItem></
TraceData><
System.Diagnostics xmlns="http://schemas.microsoft.com/2004/08/System.Diagnostics"><
Timestamp>22316850648</Timestamp><
Callstack>at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Diagnostics.TraceEventCache.get_Callstack()
at System.Diagnostics.XmlWriterTraceListener.WriteFooter(TraceEventCache eventCache)
at System.Diagnostics.XmlWriterTraceListener.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data)
at System.Diagnostics.TraceSource.TraceData(TraceEventType eventType, Int32 id, Object data)
at System.ServiceModel.Diagnostics.MessageLogger.LogInternal(MessageLogTraceRecord record)
at System.ServiceModel.Diagnostics.MessageLogger.LogMessageImpl(Message& message, XmlReader reader, MessageLoggingSource source)
at System.ServiceModel.Diagnostics.MessageLogger.LogMessage(Message& message, XmlReader reader, MessageLoggingSource source)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.Channels.FramingDuplexSessionChannel.TryReceiveAsyncResult.OnReceive(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.Channels.SynchronizedMessageSource.ReceiveAsyncResult.OnReceiveComplete(Object state)
at System.ServiceModel.Channels.SessionConnectionReader.OnAsyncReadComplete(Object state)
at System.ServiceModel.Channels.TracingConnection.TracingConnectionState.ExecuteCallback()
at System.ServiceModel.Channels.TracingConnection.WaitCallback(Object state)
at System.ServiceModel.Channels.SocketConnection.FinishRead()
at System.ServiceModel.Channels.SocketConnection.AsyncReadCallback(Boolean haveResult, Int32 error, Int32 bytesRead)
at System.ServiceModel.Channels.OverlappedContext.CompleteCallback(UInt32 error, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</
Callstack></
System.Diagnostics></
ApplicationData></
E2ETraceEvent>Is there anyone out there that has encountered such a problem? (And found a solution?)
The same dataset passes over a simple asmx webservice without any problem...
Thnx for any help!
Answers
I will take a look at the threads you mentioned.
The problem is though, that it is not my choice to make. I'm "converting" a webservice of my company to a wcf service and I'm pretty much stuck with those datasets. A big part of the methods that are exposed in the service accept and almost all of them return a dataset. 99% of those functions work. Just the one with the anytype column.
A messy workaround, by the way, is writing the dataset with its schema into a string at clientside and reading it back to a dataset at serverside...
Anyways, thnx for trying to help!
All Replies
Hi,
Did you add the KnownType attribute to your data contract?
I'm sorry, I thought my post was pretty clear.
I do NOT have a datacontract. It's a "good old fashioned" dataset I'm trying to send.
And as I already mentioned, in general, datasets get from client to service (and the other way around) without any problem.
It's just when there's ONE column of a table in that dataset that's an object (so ANYTYPE) that things go wrong...Sorry, I misinterpreted your question. As a general rule, it is often better to use custom collections instead of DataSets. Take a look at these threads:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126054&SiteID=1
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=583867&SiteID=1Is it possible to simply subclass and then specify the KnownType attribute under the DataMember? Or are you set on working with DataSets?
I will take a look at the threads you mentioned.
The problem is though, that it is not my choice to make. I'm "converting" a webservice of my company to a wcf service and I'm pretty much stuck with those datasets. A big part of the methods that are exposed in the service accept and almost all of them return a dataset. 99% of those functions work. Just the one with the anytype column.
A messy workaround, by the way, is writing the dataset with its schema into a string at clientside and reading it back to a dataset at serverside...
Anyways, thnx for trying to help!
I realise that this is an old thread, by I'm adding the following in the hope that it will be of some benefit to others in the future.
A couple of days ago I encountered exactly the same problem as described here and, like others, can't easily abandon the use of typed DataSets in an application (written by someone else) which I'm porting from Web Services to WCF. I've done quite a bit of digging into the problem and now understand what's happening.
The XML fragment below represents an instance of a DataColumn within a serialized typed DataSet, where the data type can vary from row to row (the underlying SQL Server table uses a SQL_VARIANT).
<Data xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1</Data>
During de-serialization, System.Data.Common.ObjectStorage.ConvertXmlToObject correctly identifies that fact that a type attribute within the http://www.w3.org/2001/XMLSchema-instance namespace exists on the element. It then parses the value of the type attribute (which, in our example, is "xs:int") by splitting it at the colon. It performs a namespace look-up using the namespace prefix ("xs" in our case) to confirm that it maps to "http://www.w3.org/2001/XMLSchema". This lookup, performed ultimately by the LookupNamespace method of the System.Xml.XmlBinaryReader implemented within WCF's System.Runtime.Serialization.dll, fails. The reason it fails is that the binary representation of the <Data> element is incorrect in the stream provided by WCF. (Note that I'm using "netTcpBinding" and therefore binary message encoding.)
As you can see from the XML fragment above, the <Data> element contains one fully-qualified attribute (prefix="xsi" localname="type" value="xs:int") and two xmlns attributes (prefix="xs" uri="http://www.w3.org/2001/XMLSchema" and prefix="xsi" uri="http://www.w3.org/2001/XMLSchema-instance"). But the binary stream represents the <Data> element as containing one fully-qualified attribute (prefix="xsi" localname="type" value="xs:int"), one unqualified attribute (localname="xmlns:xs" value="http://www.w3.org/2001/XMLSchema") and one xmlns attribute (prefix="xsi" uri="http://www.w3.org/2001/XMLSchema-instance"). The localname of "xmlns:xs" is clearly invalid, and prevents the "xs" namespace being loaded into the NamespaceManager and found via the LookupNamespace method.
Interestingly, this isn't a bug in WCF at all - but in System.Data.
The System.Xml.XmlWriter abstract base class (which the WCF System.Runtime.Serialization.XmlSerializableWriter derives from) provides three WriteAttributeString overloads to write attributes into the stream which allow various combinations of namespace prefix, namespace URI, local name and value to be specified. If System.Data is writing to a text-based writer, it really doesn't matter if it's a little casual about which overload it calls (which it occassionally is), it'll still get the correct text generated. For example, if it specifies a local name of "xmlns:xs" (which is actually a namespace prefix and a local name) and a value of "http://www.w3.org/2001/XMLSchema" it'll get the following text: xmlns:xs="http://www.w3.org/2001/XMLSchema", which is the same as it would have got via the correct overload. Whilst it gets away with it for text-based writers, using the wrong overload with a binary writer generates the wrong binary tokens, meaning the deserialization fails.
I've worked around this by creating a tiny implementation of System.Xml.XmlWriter which simply delegates every method call to an inner System.Xml.XmlWriter (which is actually a System.Runtime.Serialization.XmlSerializableWriter supplied by WCF). The only situation where it doesn't simply pass-on the method call is that described above, when it spots the incorrect overload of WriteAttributeString has been called, and calls the correct one.
It seems to work fine. The only down-side is that I need to paste a few lines of code into the auto-generated class for the typed DataSet to have it call WriteXml on my System.Xml.XmlWriter implementation, rather than the supplied one. Not ideal, but at least it keeps the solution where the problem is - in the serialization logic - rather than having to change the client and/or server business logic.
I have logged the bug with Microsoft. See https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=271636.
I believe that i am having the same problem. For me, the problem is happening in during deserialization in the XMLDiffLoader.ProcessDiffs function with a hashtable lookup.
If think that i understand what you are saying about creating a custom XmlWriter. I have created an XmlWriter that accepts an "InternalXmlWriter" then i shadow/override every public function of XmlWriter with a call to the internal XmlWriter. Except for WriteAttributeString where I check to make sure that the they havent included a ":" in the localname parameter (signifying that it is actually compont localname and namespace. If they have I extract the localname and then call the internalbase with the correct localname and namespace.
If that isnt what you are suggesting or can think of an easier way then please tell me.
My main question is how do you get WCF to serialize with the new XmlWrite. Which lines did you just paste into the DataSEt?
It sounds like your XmlWriter is doing exactly the right thing to work-around the bug. In my case, I had a class called SystemConfig.cs, auto-generated from SystemConfig.xsd. To use the revised XmlWriter, I:
modified the class declaration to indicate that IXmlSerializable is implemented by changing:
public partial class SystemConfig : System.Data.DataSet {
to
public partial class SystemConfig : System.Data.DataSet, System.Xml.Serialization.IXmlSerializable {and inserted the following IXmlSerializable implementation within the class:
#region System.Xml.Serialization.IXmlSerializable Members
void System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
base.WriteXml(new MyProject.XmlWriter(writer));
}System.Xml.Schema.XmlSchema System.Xml.Serialization.IXmlSerializable.GetSchema()
{
return null;
}void System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
base.ReadXml(reader);
}#endregion
Hope that helps.

