Data Contracts Not Matching Doesn't Cause An Error in WCF - c#

I have a WCF service that implements a data contract. I then have a client that consumes that service with it's own implementation of the data contract.
If the data contracts don't match exactly, it doesn't generate any sort of error, nor does it return any data.
public class RecipeClient : ClientBase<IRecipeService>, IRecipeService
{
public RecipeEntity[] GetAllRecipes()
{
var recipe = Channel.GetAllRecipes();
return recipe;
}
}
In the above example, after the call is made, recipe contains an empty array of RecipeEntity.
I would expect it to not return any data, but why doesn't it generate an error?

It is for backward compatibility. If you add in datacontract of existing service some not required properties, all existing clients will work without errors.

As if was mentioned it's for backward compatibility, but you can mark some properties as required. And if there is no such property in a message, an exception will be thrown:
[DataContract]
public class Recipe
{
[DataMember]
public string Name { get; set; }
[DataMember(IsRequired = true)]
public string Rank { get; set; }
}

Related

Exception when returning list of objects with servicestack

I am attempting to get ServiceStack to return a list of objects to a C# client, but I keep getting this exception:
"... System.Runtime.Serialization.SerializationException: Type definitions should start with a '{' ...."
The model I am trying to return:
public class ServiceCallModel
{
public ServiceCallModel()
{
call_uid = 0;
}
public ServiceCallModel(int callUid)
{
this.call_uid = callUid;
}
public int call_uid { get; set; }
public int store_uid { get; set; }
...... <many more properties> ......
public bool cap_expense { get; set; }
public bool is_new { get; set; }
// An array of properties to exclude from property building
public string[] excludedProperties = { "" };
}
The response:
public class ServiceCallResponse
{
public List<ServiceCallModel> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
And the service:
public class ServiceCallsService : Service
{
// An instance of model factory
ModelFactory MyModelFactory = new ModelFactory();
public object Any(ServiceCallModel request)
{
if (request.call_uid != 0)
{
return MyModelFactory.GetServiceCalls(request.call_uid);
} else {
return MyModelFactory.GetServiceCalls() ;
}
}
}
The client accesses the service with:
JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("http://172.16.0.15/");
client.SetCredentials("user", "1234");
client.AlwaysSendBasicAuthHeader = true;
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The "model factory" class is a DB access class which returns a list. Everything seems to work just fine when I access the service through a web browser. The JSON returned from the service starts:
"[{"call_uid":70...."
And ends with:
"....false,"is_new":true}]"
My question is, what here might be causing serialization/deserialization to fail?
Solution
Thanks to the answer from mythz, I was able to figure out what I was doing wrong. My misunderstanding was in exactly how many DTO types there are and exactly what they do. In my mind I had them sort of merged together in some incorrect way. So now as I understand it:
Object to return (In my case, called "ServiceCallModel": The actual class you wish the client to have once ServiceStack has done its job. In my case, a ServiceCallModel is a key class in my program which many other classes consume and create.
Request DTO: This is what the client sends to the server and contains anything related to making a request. Variables, etc.
Response DTO: The response that the server sends back to the requesting client. This contains a single data object (ServiceCallModel), or in my case... a list of ServiceCallModel.
Further, exactly as Mythz said, I now understand the reason for adding "IReturn" to the request DTO is so the client will know precisely what the server will send back to it. In my case I am using the list of ServiceCallModel as the data source for a ListView in Android. So its nice to be able to tell a ListViewAdapter that "response.Result" is in fact already a useful list.
Thanks Mythz for your help.
This error:
Type definitions should start with a '{'
Happens when the shape of the JSON doesn't match what it's expecting, which for this example:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The client is expecting the Service to return a ServiceCallResponse, but it's not clear from the info provided that this is happening - though the error is suggesting it's not.
Add Type Safety
Although it doesn't change the behavior, if you specify types in your services you can assert that it returns the expected type, e.g Change object to ServiceCallResponse, e.g:
public ServiceCallResponse Any(ServiceCallModel request)
{
...
}
To save clients guessing what a service returns, you can just specify it on the Request DTO with:
public class ServiceCallModel : IReturn<ServiceCallResponse>
{
...
}
This lets your clients have a more succinct and typed API, e.g:
ServiceCallResponse response = client.Get(new ServiceCallModel());
instead of:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
See the New API and C# Clients docs for more info.

How to get the request object from wcf message?

I have a WCf service with Contracts shown below.
[MessageContract]
public class ServiceRequest
{
[MessageBodyMember]
public int RequestId { get; set; }
[MessageBodyMember]
public OrderDetails OrderDetails { get; set; }
}
[DataContract]
public class OrderDetails
{
[IsLogRequired]
public int OrderId { get; set; }
[IsLogRequired]
public int Quantity { get; set; }
public string CustomerName { get; set; }
}
[IsLogRequired] is custom Attribute.
We need to get all properties in the request which have "[IsLogRequired]" attribute when the request is received. We want to do it as generic solution so that it can be plugged into all services.
We thought of using "MessageInspector" to do this implementing "IDispatchMessageInspector".
How do i get the actual request object from "System.ServiceModel.Channels.Message" parameter of IDispatchMessageInspector.AfterReceiveRequest() method?
Please correct me if i am using a wrong interface or wrong method. Any other solution to this?
I am assuming that "[IsLogRequired] is custom property." means a custom attribute...
Simple answer is that there is no solution to transfer custom attributes that are decorating the data contract as you described it.
Data contracts should be pure and not encumbered by business logic. The know how about the what should be done with various fields belongs to a service implementation.
Possible approach could look like this:
public class OrderService : IOrderService
{
private void ProcessOrder(Order order)
{
var ra = new AuditMetadataResourceAccess();
MethodInfo[] fieldsToLog = ra.GetLoggingFields(typeof(OrderDetal));
if (fieldsToLog.Any())
{
var logger = new LogingEngine();
logger.Log(fieldsToLog, order.OrderDetails);
}
}
}
You could move this implementation inside message inspector or operation invoker. Carlos Figueira has extensive description of each WCF extensibility point.
"How do i get the actual request object from "System.ServiceModel.Channels.Message" parameter of IDispatchMessageInspector.AfterReceiveRequest() method?"
I am assuming you are referring to Web request. WebOperationContext.Current but you need to have ASP.NET Compatibility Mode turned on.

Looking for a workaround to serializing a method or constant

Apparently my education has failed me, because I didn't realize that methods in C# cannot be serialized. (Good to know.)
I am trying to create a WCF service that returns a simple class I created. The problem is that this simple class contains methods that I want to expose, and the caller of my service won't have any access to them (assuming they won't have a .dll containing the class declaration).
public class Simple
{
public string Message { get; set; }
private const string _Hidden = "Underpants";
public string Hidden
{
get { return _Hidden; }
}
public string GetHidden()
{
return _Hidden;
}
}
I set up a WCF service (let's call it MyService) to return an instance of my Simple class. To my frustration, I'm only getting a partial build of my class back.
public void CallService()
{
using (var client = new MyService.Serviceclient())
{
Simple result = client.GetSimple();
string message = result.Message; // this works.
string hidden = result.Hidden; // this doesn't.
string fail = result.GetHidden(); // Underpants remains elusive.
}
}
Is there any type of workaround where I'm able to set up a property or method on my class that will be accessible to whomever calls my service? How does one handle constants or other methods that are set up in a class that only exists in a service?
Typically you would create three different projects.
1. Service project
2. Client project
3. Data project
The Data project contains only the data classes - no application code. The methods and constants in these data classes should be independent of the Service/Client projects.
The Data project is included as a reference in both the Service and Client projects so that serialization and deserialization happen against the same binary - and you get to retain your constants/methods/etc.
The downside here is that all your clients will either have to be .NET apps, or you will have to provide different data libraries for each platform you wish to support.
As far as I know the only things that can be returned in a WCF service are primitives or a class with public properties that have a get method on them. From a high level WCF exists to allow you to specify a contract between the client and the server that it in theory transportation agnostic (ie you can swap out an HTTP endpoint for a netTcp endpoint and the service will function the same way from a contractual level).
The question to answer then is what data are you trying to pass back in this service call. If it's an object called simple with the data points of Message and Hidden then I would advise creating a data class called Simple that has those values as properties:
[DataContract]
public class Simple
{
[DataMember]
public string Hidden { get; set; }
[DataMember]
public string Message { get; set; }
}
When the client receives the response back Message and Hidden will be populated with whatever you have set their values to on the server side.
The DataMember attribute can only be used on properties and fields. This means that a WCF response can only serialize these types.
If you really want to only use the const in your WCF contract You could convert it to a field and place the DataMember attribute on it:
public class Simple
{
[DataMember]
public string Message { get; set; }
[DataMember]
public const string Hidden = "Underpants";
}
To be able to do this the field must be accessible (public).
Add the DataMember attribute to your property. To do so, you must have both a get and a set defined.
[DataMember]
public string Hidden
{
get { return _Hidden; }
set { }
}
technically you could do
public class thingToSerialize{
public Func<ArgType1,ArgType2...,ReturnType> myFunction{get;set;}
}
and then assign it a lambda that takes the arguments and returns the return type
before serializing

Order of returned WCF JSON

I have a WCF service that returns some JSON from a serialized object:
public class Response
{
public string Token { get; set; }
public int UserId { get; set; }
...
}
I've added some extra properties to this class, but now some of the implementations fails because they read it like:
string[] ResultLoginValues = e.Result.ToString().Split(',');
and it's returned in alphabetically order instead of the old order with the new properties last.
Is there any way I can change the order, or should they rewrite the clients?
For solution refer WCF DataContract DataMember order

IExtensibleDataObject usage in clients

I have converted my web service to wcf service which has some datacontracts. As a best practice it is mentioned and advisable that the DataContracts should inherit from IExtensibleDataObject. I get the point that in case of addition or removal of datamembers, IExtensibleDataObject is helpful. But i am not able to get how will the clients access removed datamembers. Here is my code:
[ServiceContract(Namespace = "http://mycompany.com/2010/08/")]
public class MyWebService {
[OperationContract]
public Employee Add(Employee emp)
{
// Some Processing
}
}
[DataContract(Name = "Employee", Namespace = "http://mycompany.com/2010/08/")]
public class Employee : IExtensibleDataObject {
[DataMember] public string FirstName;
[DataMember] public string LastName;
public ExtensionDataObject ExtensionData { get; set; }
}
Now in my next version of web service I made some changes to DataContract as
[DataContract(Name = "Employee", Namespace = "http://mycompany.com/2010/09/")]
public class Employee : IExtensibleDataObject {
[DataMember] public string FirstName;
[DataMember] public string LastName;
[DataMember(IsRequired = true)] public string MiddleName;
public ExtensionDataObject ExtensionData { get; set; }
}
However my client that is accessing my older version of web service is now getting error for not supplying the MiddleName field. I am still confused for the usage of IExtensionDataObject.
that is incorrect usage of IExtensibleDataObject. You have modified data contract on the server side an you have marked new field as required so it means you have broken versioning and nothing helps you.
IExtensibleDataObject is for other purpose. Let assume that you have modified your client so that data contract on the client contains MiddleName. Now you set the MiddleName and use Add service operation. What value of MiddleName will be in returned Employee object? If you don't use IExtensibleDataObject the value will be null, if you use IExtensibleDataObject the value will be same as you set to input parameter.
When using DataContractSerializer WCF throws away all non understood parameters. IExtensibleDataObject avoid this by storing all those parameters in special collection and sending them back to client.
If you want to use contract versioning forget about required fields. That is the first thing which will break it.
I'm afraid that's not the correct usage of IExtensibleDataObject, the IExtensibleDataObject interface is designed to support version round-tripping, have a read of this MSDN article on forward compatibility:
http://msdn.microsoft.com/en-us/library/ms731083.aspx
And here's another article on best practices on Data Contract versioning in general:
http://msdn.microsoft.com/en-us/library/ms733832.aspx
Make ExtensionData Property "Virtual" as shown below:
public virtual ExtensionDataObject ExtensionData
{
get { return theData; }
set { theData = value; }
}

Categories