We have a WCF service which includes Serializable classes in a contract that has DataContract and DataMember at the root level of the service.
While trying to build a solution to isolate the problem, I came across the following:
[ServiceContract]
public interface IService1
{
[OperationContract]
CompositeType GetDataUsingDataContract();
}
[DataContract]
public class CompositeType
{
[DataMember]
public MyType MyProperty { get; set; }
}
[Serializable]
public class MyType
{
private int amount1;
[XmlElement(Form = XmlSchemaForm.Unqualified, DataType = "int", ElementName = "AmountN")]
public int Amount1
{
get
{ return amount1; }
set
{ amount1 = value; }
}
}
Gives the following xsd:
<xs:complexType name="CompositeType">
<xs:sequence>
<xs:element name="MyProperty" type="tns:MyType" nillable="true" minOccurs="0"/>
</xs:sequence>
</xs:complexType><xs:element name="CompositeType" type="tns:CompositeType" nillable="true"/>
<xs:complexType name="MyType">
<xs:sequence>
<xs:element name="amount1" type="xs:int"/>
</xs:sequence>
</xs:complexType>
<xs:element name="MyType" type="tns:MyType" nillable="true"/>
</xs:schema>
Question is: Why is the private but not the public member being serialized?
Serializers and serialization attributes are two different things.
XmlElement is attribute for XmlSerializer but it has no meaning for DataContractSerializer or for BinaryFormatter. XmlElementAttribute Class
DataContractSerializer can serialize multiple types, but it uses own serialization algorithm Link. When serializing objects marked with [Serializable],
DataContractSerializer follows default serialization pattern (serializing all members, [NonSerialized] applies). If you need more control then you can implement ISerializable for custom serialization and then you can set node names and values in serialized object Serialization .
There is also an option to implement IXmlSerializable and have full control on how serialized object will look like.
This msdn article can explain, why wcf is able to serialize your MyType:
Types Supported by the Data Contract Serializer: . . .
Types marked with the SerializableAttribute attribute. Many types
included in the .NET Framework base class library fall into this
category. The DataContractSerializer fully supports this serialization
programming model that was used by .NET Framework remoting, the
BinaryFormatter, and the SoapFormatter, including support for the
ISerializable interface.
And since wcf have no problem to serialize private fields, probably this is the reason of serializing your privet field amount1.
INO, The question is "why your property Amount1 was not serialized?" I'd try to rename it (be different from field name), remove xml attributes on it, and re-try.
Related
In my web service I want to get rid of the generated ArrayOf... definitions in the WSDL which are generated by calling ...service.svc?singleWsdl.
Currently the definiton looks like (and I tried all varieties using XmlArray, etc.):
[DataContract]
public class Incident {...}
[CollectionDataContract(Name = "IncidentList", ItemName = "IncidentItem")]
public class IncidentList : List<Incident>
{
public IncidentList()
: base()
{ }
public IncidentList(IEnumerable<Incident> list)
: base(list)
{ }
}
[MessageContract]
public class IncidentsResponse
{
[MessageBodyMember]
public Incident[] Incidents { get; set; }
[MessageBodyMember]
public IncidentList IncidentList { get; set; }
}
When I get the WSDL I always receive (more or less - depending on Name attributes, etc.):
<xs:element name="IncidentsResponse">
<xs:complexType><xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="IncidentList" type="tns:ArrayOfIncident"/><xs:element minOccurs="0" maxOccurs="1" name="Incidents" type="tns:ArrayOfIncident"/>
</xs:sequence>
</xs:complexType></xs:element>
What I would really like to have is the types listed directly inside the element like
<xs:element name="IncidentsResponse">
<xs:complexType>
<Incidents><IncidentItem>...</IncidentItem><IncidentItem>...</IncidentItem> </Incidents>
<IncidentList><IncidentItem>...</IncidentItem><IncidentItem>...</IncidentItem></IncidentList>
</xs:complexType></xs:element>
So a reference to the actual data as such, not the list type (ArrayOf).
Any way to achieve this? The CollectionDataContract attribute is supposed to do the trick if I get the info right, but somehow it doesn't...
Especially as the consuming client is Java this extra indirection is hurting a bit as it bloats the code
Any ideas welcome :-)
this is intended since wcf can be used across language so it should support common types rather than only supporting .net specific type.
still if u want to generate a proxy using svcutil with list then you can create it by using the sample svcutil wsdl /ct:System.Collections.Generic.List`1 or if you are using adding service reference then there is an advanced button , Click that button and select the System.Generic.List as your Collection. This will resolve the problem.. and having the array information will not harm anything so it can be left as it is.
I have a question regarding WCF and deserialisation of XML.
Lets say i have an XSD that specifies a number of attributes as minoccurs='0'.
<xs:element name=TestData>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs=0 name="stoppageHours>
<xs:simpleType>
<xs:restriction base="xs:int">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element minOccurs=0 name="stoppageDate>
<xs:simpleType>
<xs:restriction base="xs:dateTime">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:sequence>
<xs:complexType>
</xs:element>
If i dont have a value for one for the attributes i can omit the element tags in the XML i.e.
<TestData>
<StoppageHours>26</StoppageHours>
...omitted stoppageDate...
</TestData>
But, given that i have generated classes from the XSD to .NET c#, when i post the XML to my web service it complains that deserialisation has failed as it was expecting element ?
How can you force the deserialistion process to ignore the missing XML element and set the class memeber in question to a NULL value? I have tried setting the class members to system nullable types but to get this to work i have to pass the XML element as xsi:nillable? What i really want to be able to do is simply omit the XML tag.
Thanks.
The attribute that allows this functionatlity is called EmitDefaultValue. Your data contract on the service side will probably look something like this:
[DataContract]
public class TestData
{
[DataMember(EmitDefaultValue = false)]
public int StoppageHours { get; set; }
[DataMember(EmitDefaultValue = false)]
public DateTime StoppageDate { get; set; }
}
When EmitDefaultValue is equal to false, it tells the data contract serializer in WCF to remove the elements from the input if their value is equal to the default value. For value types the serializer will on the server side set the value to it's default value if it's not present. So in this scenario if you pass
<TestData></TestData>
when it is received by WCF your object will have these values server side:
<TestData>
<StoppageHours>0</StoppageHours>
<StoppageDate>1/1/0001 12:00:00 AM</StoppageDate>
</TestData>
Edit: The other thing you may need to do based on your feedback comment is set the IsRequired attribute on the DataMember as well. When I tested this attribute should default to false but you can try and be explicit in the contract definition. The updated contract would now look like this:
[DataContract]
public class TestData
{
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public int StoppageHours { get; set; }
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public DateTime StoppageDate { get; set; }
}
When the value is set to true and the nodes are missing I get a serialization exception. When IsRequired is set to false I can send an empty request:
<TestData></TestData>
and there is no serialization exception. If this doesn't work then you may need need to clear your temporary asp.net files and restart the service. I had that issue when testing this out. Hope this helps.
hello I have WCF service with webHttpEndpointBehavior under Sitecore 6 and I use Glass Mapper to read items, Glass mapper has depends on Castle library.
It is working well, but i have some methods with Contract like:
[OperationContract]
[WebInvoke(Method = "POST",BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
List<Shade> GetAllShades(int columns, int rows);
where I try return list of my class in JSON format,
during debug my service with SvcTraceViewer.exe I find out next error:
There was an error while trying to serialize parameter
:GetFamilyShadesResult. The InnerException message was 'Type
'Castle.Proxies.ShadeProxy' with data contract name
'ShadeProxy:http://schemas.datacontract.org/2004/07/Castle.Proxies' 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.'. Please see
InnerException for more details.
How to resole this issue? Castle.Proxies.ShadeProxy is a dynamic class under Castle and I can't use KnownTypeAttribute.
Actually if I JSON.net library and return results as string everything works fine.
I assume you have a class that links to other classes loaded via Glass, e.g.
[SitecoreClass]
public class Shade{
[SitecoreField]
public virtual IEnumerable<AnotherClass> SomeField{get;set;}
[SitecoreChildren]
public virtual IEnumerable<AnotherClass> Children{get;set;}
}
[SitecoreClass]
public class AnotherClass{}
To allow lazy loading of classes Glass uses proxies generated by Castle, so at runtime when the classes are loaded into the SomeField property you actually get a sub-class of the AnotherClass class.
To resolve this you have to explicitly tell class to load the other classes as concrete types, updating the SitecoreField attribute should fix this problem:
[SitecoreClass]
public class Shade{
[SitecoreField(Setting=SitecoreFieldSettings.DontLazyLoad)]
public virtual IEnumerable<AnotherClass> SomeField{get;set;}
[SitecoreChildren(IsLazy=false)]
public virtual IEnumerable<AnotherClass> Children{get;set;}
}
[SitecoreClass]
public class AnotherClass{}
Fairly new to WCF and need help with understanding why serialisation is not working correctly.
Service definition - I just want to post, serialise into a LogDeviceCommunication object and then just return the object as a simple test
[OperationContract]
[WebInvoke(UriTemplate = "AddDeviceCommunicationLog", RequestFormat =
WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare, Method = "POST")]
LogDeviceCommunication AddDeviceCommunicationLog(LogDeviceCommunication
deviceCommunicationEntry);
public LogDeviceCommunication AddDeviceCommunicationLog(LogDeviceCommunication
deviceCommunicationEntry)
{
return deviceCommunicationEntry;
}
At the moment I am just posting the following XML with Fiddler as a test.
<?xml version="1.0" encoding="UTF-8"?>
<LogDeviceCommunication>
<ID>1207a26e-ab59-4977-b7eb-b2776205cffe</ID>
<DeviceID>A42E8707-7C65-45AA-8E58-5D21F53DA101</DeviceID>
<Time>2012-03-14T15:38:28.379Z</Time>
<Line>0</Line>
<Tab>0</Tab>
<Info>Starting Synchronisation</Info>
</LogDeviceCommunication>
Results returned from Fiddler
<LogDeviceCommunication z:Id="i1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<ChangeTracker z:Id="i2"
xmlns:a="http://schemas.datacontract.org/2004/07/conxEntities">
<a:ExtendedProperties/>
<a:ObjectsAddedToCollectionProperties/>
<a:ObjectsRemovedFromCollectionProperties/>
<a:OriginalValues/>
<a:State>Added</a:State>
</ChangeTracker>
<DeviceID>00000000-0000-0000-0000-000000000000</DeviceID>
<ID>1207a26e-ab59-4977-b7eb-b2776205cffe</ID>
<Info i:nil="true"/>
<Line i:nil="true"/>
<Tab i:nil="true"/>
<Time>2012-03-14T15:38:28.379Z</Time>
</LogDeviceCommunication>
Why does the DeviceID contain the 0000's (I assume it's a null Guid) while the ID contains the correct Guid; also why do the Info, Line and Info elements contain nil values?
The LogDeviceCommunication is a POCO generated from EF4 using the ADO.NET Self Tracking Template
Condensed version is
[DataContract(IsReference = true, Namespace = "")]
public partial class LogDeviceCommunication: IObjectWithChangeTracker,
INotifyPropertyChanged
[DataMember]
public System.Guid DeviceID
[DataMember]
public System.DateTime Time
[DataMember]
public Nullable<int> Line
[DataMember]
public Nullable<int> Tab
[DataMember]
public string Info
[DataMember]
public System.Guid ID
I am sure I am doing something incorrectly so any help appreciated.
The problem lies in the required ordering of the XML.
WCF Datacontract, some fields do not deserialize
http://neimke.blogspot.co.nz/2012/03/serialization-ordering-causes-problems.html
When WCF receives your request, its deserialization machinery will create a new instance of the LogDeviceCommunication type to populate with the values it receives. It seems that the code from the EF partial class of your instance is being triggered and it results in what you post in your question.
Try setting a debugger break point on the return statement in your AddDeviceCommunicationLog method to see what EF & WCF deserialized for you. If it's just as what you posted then the issue is likely caused by the EF plumbing code. Also, you may want to enable WCF message tracing to see what WCF is actually receiving and sending back.
EDIT: Just ran across this blog post that shows some of the interaction between EF & WCF. You may want to review it to see if it's applicable to your issue.
I bet the other parts of the class, generated by that template, include the elements you're seeing.
In general, it's not a good idea to return EF entities (or any complex .NET type) from a web service - they drag along implementation dependencies. Return a purely POCO class as a DTO instead.
I want to expose enum attributes to WCF client application, but I can only see enum values.
Here is the enum:
public enum TemplateType
{
[EnumDescription("Property Particulars")]
[EnumValue("PropertyParticulars")]
PropertyParticulars = 1,
[EnumDescription("Short Format Lists")]
[EnumValue("ShortFormatLists")]
ShortFormatLists,
[EnumDescription("Client Letters")]
[EnumValue("ClientLetters")]
ClientLetters,
[EnumDescription("Labels")]
[EnumValue("Labels")]
Labels
}
How can I expose the Description and Value attributes?
You can expose enums from a service but the attributes on an enum are not serialized when they are sent over the wire. This means that consumers of this enum will only see the enum itself and none of your attributes.
What you need to do is dress up your enum with a DataContract attribute and the values with the EnumMember attribute so that your information will be serialized, but this will only allow you to specify the underlying value of each enum value, not a description.
There is a workaround if the intention is to expose a display text for enum members, define your enum in this way in the contracts:
public enum EPaymentCycle
{
[EnumMember(Value = "Month by Month")]
Monthly,
[EnumMember(Value = "Week by Week")]
Weekly,
[EnumMember(Value = "Hour by Hour")]
Hours
}
The SvcUtils serialization produces an interesting result:
public enum EPaymentCycle : int
{
[System.Runtime.Serialization.EnumMemberAttribute(Value="Month by Month")]
MonthByMonth= 0,
[System.Runtime.Serialization.EnumMemberAttribute(Value="Week by Week")]
WeekbyWeek= 1,
[System.Runtime.Serialization.EnumMemberAttribute(Value="Hour by Hour")]
HourbyHour = 2
}
You can read the EnumMemberAttribute Value by reflection and there you got it. Also the xsd metadata file produced by svcutil serialization is as expected:
<xs:simpleType name="EPaymentCycle">
<xs:restriction base="xs:string">
<xs:enumeration value="Month by Month" />
<xs:enumeration value="Week by Week" />
<xs:enumeration value="Hour by Hour" />
</xs:restriction>
I'm not fully versed in the specs, but I doubt this kind of metadata has an equivalent representation in WSDL. Thus, this will not be visible on the client side if you generate the types in your proxy.
However, if you put all your DataContracts in a separate assembly that you reference in the client, you can reuse those types on the client side. In that case, the attributes would be visible. "Reuse types in referenced assemblies" needs to be checked for your Service Reference, but this is on by default.
Here's a short blog post about it. I'm sure there are others...
Example enum for the values of a traffic light...
[DataContract]
public enum TrafficLightType
{
[EnumMember]
Red,
[EnumMember]
Green,
[EnumMember]
Amber
}