How to fetch value from an ExtensionDataObject of a wcf reponse - c#

I have a WCF service which returns ExtensionDataObject during runtime as attached snapshot:
Im struck with fetching value for these objects. Could anyone please help here:
Have tried with below code using reflection, which throws Parameter count missing exception
List<System.Runtime.Serialization.ExtensionDataObject> extData = temp.Select(x => x.ExtensionData).ToList();
var GetCountry = extData.GetType().GetProperties();
string Country = string.Empty;
foreach (var property in GetCountry)
{
string name = property.Name;
object value = property.GetValue(extData, null);
if (name == "Country")
Country = value.ToString();
}

The Extensiondataobject field is generated to control the data contract incompatibility between the server and the client, so it will return a field named extensiondataobject. In other words, your client data contract implements the IExtensionDataObject interface.
[DataContract(Namespace="abcd")]
public class Product: IExtensibleDataObject
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Name { get; set; }
public ExtensionDataObject ExtensionData { get ; set ; }
}
If we capture this request through Fiddle, you can even see all the data directly.
In a word, you only need to add the Country property to the Data class of X object. It will be deserialized automatically. This class should be your client-side data contract class, instead of the server-side data class.
Finally, it seems that the value of these fields is null. We should ensure that the server and client data contracts have the same namespace. It cannot be the default value(http://tempuri.org). As I defined above, this namespace attribute should be consistent with the server-side value.
Feel free to let me know if there is anything I can help with.

Related

Having an old property name accepted - WFC/SOAP

I am using WCF and .NET 4.5, working with a WCF SOAP web service that's been in production for some time. Need to rename a property, but there is a small chance that some of production customers may have the old property name in their requests.
For example, if a property was called MyPoorlyNamedProperty and I renamed it to MyProperty, I wish both MyPoorlyNamedProperty and MyProperty in SOAP would de-serialize to that same property.
Is there a way to decorate a property of a type in WCF to specify an "alternative" accepted name for the renamed property?
I don't know any alternative name decoration or anything similar. If you wish to create a breaking-change, you need to do one of both:
Change the property name and notify your clients that the contract was changed and they need to prepare accordingly
Modify your API to accept both properties. In case it recieves a request with value for the old property, set this value to the new property.
Solution 2 is ugly and not the best practice. I don't recommend doing it.
You can add the second property to your model. And work with get; set; and the DataMember attribute.
[DataContract]
public class Person
{
[DataMember]
private string _surname;
public string Surname { get { return this._surname; } set { this._surname = value; } }
public string Lastname { get { return this._surname; } set { this._surname = value; } }
}

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.

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

One property not saving when using web service

I have this object:
public class Announcement
{
public int Id { get; set; }
public DateTime DateSent { get; set; }
private IList<string> _recipients;
public IList<string> Recipients
{
get { return _recipients; }
set { _recipients = value; }
}
public string RecipientsString
{
get { return String.Join("\n", _recipients); }
set { _recipients = value.Split('\n').ToList(); }
}
}
I can populate this object with the DateSent and RecipientString (a string of email addresses separated by \n) and save it to the database with no problems.
Now I want to move this to a web service so we can use it across multiple apps.
I created the exact same object in the webservice, and testing locally (on the service) everything works as expected.
But if I populate the object on the client and pass it to the service to be saved, the RecipientString is always empty (not null). The DateSent is fine.
I'm guessing the data is getting lost in serialization, but I don't know why, or how to solve this. I thought also, it could have something to do with the # in the email address, but I've ruled that out. Any suggestions?
This happens because de WSDL that is generated to describe your service can't describe the function that is used in your get and set functions. I suggest you keep RecipientsString as a common property, and create a private method GetRecipients on your class that processes the RecipientsString value and returns the list you need.
Use RecipientsString without backing field.

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