I am trying to validate the WCF service request using System.ComponentModel.DataAnnotations.dll of Version v4.0.30319. I am using VS2010 with Target Framework v4.0.
Below are my sample request(s). If i invoke the service operation using WcfTestclient the annotations are not firing even if i pass the invalid values (null/String.Empty/"") for Name.
Request1:
[MessageContract]
public class AddUserRequest
{
[MessageBodyMember]
[Required(ErrorMessage = "Id is required.")]
public int Id { get; set; }
[MessageBodyMember]
[Required(ErrorMessage = "Name is required.")]
[StringLength(100, MinimumLength = 10, ErrorMessage = "Name length should be between 10 and 100")]
public string Name { get; set; }
}
Request2:
[DataContract]
public class User
{
[DataMember(IsRequired = true)]
[Required(ErrorMessage = "Id is required.")]
public int Id { get; set; }
[DataMember(IsRequired = true)]
[Required(ErrorMessage = "Name is required.")]
[StringLength(100, MinimumLength = 10, ErrorMessage = "Name length should be between 10 and 100")]
public string Name { get; set; }
}
Is i am missing some thing over here ?. Please suggest.
From msdn:
The System.ComponentModel.DataAnnotations namespace provides attribute
classes that are used to define metadata for ASP.NET MVC and ASP.NET
data controls.
Attributes from that namespace are not handled out-of the box by WCF.
You have to write Your own logic to achieve this.
Here is an article describing custom validation of parameters for WCF.
Fortunately others have already done this so here is a CodePlex project that combines WCF and Data Annotation classes. This is probably what You need.
Edit:
DataMember.IsRequired indicates that given member must be present in the model. Not that it must have a value.
This is for your api versioning.
For example You could have had in Your Service version 1 a model like this:
[DataContract]
public class User
{
[DataMember]
public int Id { get; set; }
}
This would serialize to (in simplified way):
<User>
<Id>19</Id>
</User>
And any client that was integrated with Your service would send You xml like this.
But then You changed Your model in Your version 2 to:
[DataContract]
public class User
{
[DataMember]
public int Id { get; set; }
[DataMember]
public String Name { get; set; }
}
But Your client knows nothing about new version and sends You old xml. That xml will deserialize properly to Your new model but with Name equals null.
To inform Your old clients about this change You would add IsRequired=true to Your Name property. That way WCF will return an error for old xml and will accept only this structure:
<User>
<Id>19</Id>
<Name>Some Name</Name>
</User>
Please not that this will not make something like this invalid:
<User>
<Id>0</Id>
<Name />
</User>
Which is what is happening in Your case. Id is not nullable so it has a default value of 0.
And Name is serialized as having no value - not as "not being there".
Related
I am developing a Wcf Restful Service which contains data contract "User" shown below
[DataContract]
public class User
{
public User()
{
}
[DataMember(Name = "Name")]
public string Name { get; set; }
[DataMember(Name = "Mobile")]
public string Mobile { get; set; }
[DataMember(Name = "Email")]
public string Email { get; set; }
[DataMember(Name = "IsImageUpdated")]
public bool IsImageUpdated { get; set; }
}
Now i would like to add one mode data member of type Image,When i try to add Image with type Stream it showing exception
[DataMember(Name = "Iamge")]
public Stream Image { get; set; }
"The InnerException message was 'Type 'System.IO.FileStream' with data contract name 'FileStream:http://schemas.datacontract.org/2004/07/System.IO' 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."
The service i am developing having many Data contract's,I read some posts which saying the issue can be resolved by changing the Datacontract to message contract,Does a service supports different contract types(like Data,Message).
i need a solution.
This is not possible when using a WebHttpBinding.
Combining streamed and buffered Content is only possible when the binding has a SOAP message Format and you use MessageContract instead of DataContract.
Using a byte[] or returning the stream directly is supported.
[DataMember(Name = "Iamge")]
public byte[] Image { get; set; }
or
[OperationContract]
[WebGet(UriTemplate = "/Image")]
Stream GetImage();
or when using NetTcpBinding, WsHttpBinding, BasicHttpBinding, ...
[MessageContract]
public class ImageData
{
[MessageBodyMember]
public Stream Image { get; set; }
[MessageHeader]
public string Name { get; set; }
}
I have a XML structure like this:
<Message>
<Messagehead>
<OSType>Android</OSType>
<RouteDest>SiteServerName</RouteDest>
<ActionType>Enroll</ActionType>
</Messagehead>
<MessageBody>
<Raw>
<![CDATA[OrienginalMessageContent]]>
</Raw>
</MessageBody>
</Message>
and I want upload this XML to WCF 4.0 my rest service:
public string Enroll(Message instance)
{
// TODO: Add the new instance of SampleItem to the collection
return "success";
}
the Message is a DataContract type, I setup it like below:
[DataContract(Namespace = "")]
public class Message
{
[DataMember]
public MessageHead MessageHead { get; set; }
[DataMember]
public MessageBody MessageBody { get; set; }
}
public class MessageHead
{
public OSType OSType { get; set; }
public string RouteDest { get; set; }
public Action Action { get; set; }
}
public class MessageBody
{
public string RawRequestContent { get; set; }
}
but when I get the Message instance from the server side, all the property is null, except the OSType, can anybody tell me why? How could I solve this problem?
Besides being a really bad name for a class (since it's already used in the WCF runtime), your Message class also has some flaws:
<Message>
<Messagehead>
....
</Messagehead>
Your <Messagehead> has a lower-case h in the middle - yet your class defines it to be upper case:
[DataContract(Namespace = "")]
public class Message
{
[DataMember]
public MessageHead MessageHead { get; set; }
This will not work - case is important and relevant in a WCF message! If your XML has a lower-case h, so must your DataContract class!
Your XML also requires a <Raw> tag inside your <MessageBody>
<MessageBody>
<Raw>
<![CDATA[OriginalMessageContent]]>
</Raw>
</MessageBody>
yet your data contract doesn't respect that:
public class MessageBody
{
public string RawRequestContent { get; set; }
}
Again - those don't line up! Names are important - and they must match between your XML representation of the message, and the C# class representing that message.....
I got a WCF service with a method (GetUserSoftware)to send a List to a client.
the software I have defined like this:
[DataContract]
public class Software
{
public string SoftwareID { get; set; }
public string SoftwareName { get; set; }
public string DownloadPath { get; set; }
public int PackageID { get; set; }
}
the method is going through my db to get all software availeble to the clien, and generates a list of that to send back to the client.
problem is i on the client side the list is turned into an array. and every item in that array dont contain any of my software attributs.
i have debugged my way through the server side. and seen that the list its about to send is correct. with the expected software and attributs in it.
any one know how to work around this or know what i can do ?
Did you forget [DataMemeber] attribute on your properties?
When you use DataContract attribute for a type you have to use DataMember attribute for each property or field you want to serialize and transfer between service and client. Collections are by default created as arrays. If you don't like it you can change this behavior in Add Service Reference window -> Advanced settings where you can select which collection type should be used.
First off, each of the properties that you want to serialize should have the [DataMember] attribute:
[DataContract]
public class Software
{
[DataMember]
public string SoftwareID { get; set; }
[DataMember]
public string SoftwareName { get; set; }
[DataMember]
public string DownloadPath { get; set; }
[DataMember]
public int PackageID { get; set; }
}
Second, the translation to an Array would be handled by the client, not the server.
You can mantain List instead of array on the clien when you add the Service Reference: click the "advanced" button and change the collection type to the one you want.
I was suffering with same problem and now I solved it! It was a ServiceKnownType problem. If you have a in known type loader we have to add runtime Type like;
Type aaa = Type.GetType("System.Collections.Generic.List`1[[ProjectName.BusinessObjects.Bank, ProjectName.BusinessObjects, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null]]");
knownTypes.Add(aaa);
Anyone having same problem can try this. It's working in my environment!
I have a couple of classes (for now) and I'm trying to clear up a circular reference between the two since it is killing WCF's serialization.
I am using EF with POCOs in a WCF REST service is that helps. I have simplified my problem down to bare bones for an easy example here:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Groups
{
[WebGet(UriTemplate = "")]
public Message GetCollection()
{
var message = new Message { Body = "Test message" };
var group = new Group { Title = "Title of group" };
message.Group = group;
group.Messages = new List<Message> { message };
return message;
}
}
public class Message
{
public string Body { get; set; }
public Group Group { get; set; }
}
[DataContract(IsReference = true)]
public class Group
{
public string Title { get; set; }
public ICollection<Message> Messages { get; set; }
}
I have added the [DataContract(IsReference = true)] to the Group class so that the circular reference is cleaned up however my returned results end up like this:
<Message xmlns="http://schemas.datacontract.org/2004/07/LmApi" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Body>Test message</Body>
<Group z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"/>
</Message>
Where are the properties of the Group and how can I get them?
BritishDeveloper,
There are no properties associated with Group. That's why all you see is the ID of 1.
The reason is that as soon as you annotate the Group class with [DataContract(IsReference = true)], you are telling the DataContract serializer that it's no longer a POCO type. It's a DataContract type.
So, to serialize Group with properties, you now need to go ahead and annotate the Title and Message properties with DataMemberAttribute.
An alternative would be use the "preserveObjectReferences", which you can pass as a parameter to DataContractSerializer, DataContractSerializerOperationBehavior, and other classes.
Hope this helps!
I decided to make my own smaller classes that have a constructor that takes an entity and sets all of this lighterweight properties correctly.
Basically it is a very small copy of the class that has just the properties needed in the payload. (Obviously I have excluded the problem navigation properties)
This is similar to Circular References and WCF Here is my answer modified for this case
I had the same problem and resolved it by excluding the navigation property back to the parent from the DataContract
[DataContract]
public partial class Message
{
[DataMember]
public virtual string Body { get; set; }
[DataMember]
public virtual Group Group { get; set; }
}
[DataContract]
public partial class Group
{
[DataMember]
public virtual string Title { get; set; }
public virtual ICollection<Message> Messages {get; set;}
}
I know this question is been asked before here but still I'm not sure what to select.
My service will be called from many 3 party system in the enterprise. I'm almost sure the information the service will collect (MyBigClassWithAllInfo) will change during the products lifetime. Is it still a good idea to expose objects?
This is basically what my two alternatives:
[ServiceContract]
public interface ICollectStuffService
{
[OperationContract]
SetDataResponseMsg SetData(SetDataRequestMsg dataRequestMsg);
}
// Alternative 1: Put all data inside a xml file
[DataContract]
public class SetDataRequestMsg
{
[DataMember]
public string Body { get; set; }
[DataMember]
public string OtherPropertiesThatMightBeHandy { get; set; } // ??
}
// Alternative 2: Expose the objects
[DataContract]
public class SetDataRequestMsg
{
[DataMember]
public Header Header { get; set; }
[DataMember]
public MyBigClassWithAllInfo ExposedObject { get; set; }
}
public class SetDataResponseMsg
{
[DataMember]
public ServiceError Error { get; set; }
}
The xml file would look like this:
<?xml version="1.0" encoding="utf-8"?>
<Message>
<Header>
<InfoAboutTheSender>...</InfoAboutTheSender>
</Header>
<StuffToCollectWithAllTheInfo>
<stuff1>...</stuff1>
</StuffToCollectWithAllTheInfo>
</Message>
Any thought on how this service should be implemented?
Thanks Larsi
If the information is going to change during the lifetime but you are under the gun to get something up there I would simply create a message that had a list of variant types that could be sent along with a message type version number.. Your bus can view the version number and route it appropriately. That way customers that are stuck with older versions of the message will not have to change the message interface they are using.