Mapping Complex Type in REST Enabled WCF Service - wcf

I m trying to rest-ify our existing WCF service and one of the hurdle is mapping complex type using Uritemplate. For example take a look at the below code
[DataContract]
public class Stock
{
[DataMember]
public string Symbol { get;set; }
[DataMember]
public double FaceValue { get; set; }
}
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "?Symbol={stk.Symbol}")]
void Test1(Stock stk);
}
The above Uritemplate declaration will definitely not work, but this is what is my intention of mapping the input query variable to one of the property of that object.. Is this possible ?
Tks in advance for your help.

The example you give doesn't illustrate the problem. In that case, all that's needed is to pass a stock symbol, a simple string, and there's no need for a complex type.
In cases where you want to pass a more complex set of data, a JSON-encoded object, use WebInvoke with PUT or POST, a non-parameterized UriTemplate, and pass form data. See this answer for details.
In cases where you want GET, and can pass a small set of discrete parameters, you can use WebGet, an appropriate UriTemplate, and do the manual mapping. like so:
public enum OptionFlavor { Put, Call }
public class OptionInqury { public String Symbol; public String Month; public OptionFlavor Flavor;}
[OperationContract]
[WebGet(UriTemplate = "/optionquote/{stockSymbol}/{month}/{flavor}")]
void GetOptionPrice(string stockSymbol, string month, string flavor)
{
var x = new OptionInquiry {
Symbol = stockSymbol,
Month = month,
Flavor = (flavor.Equals("put")) ? OptionFlavor.Put : OptionFlavor.Call
};
// off you go...
}

If you want to map your query string parameters to the properties in your object, you need to go with WebInvoke. Unfortunately with GET, you will have to do this manually.

Related

Strategy pattern to consume REST API

I have to consume two different REST API providers about VoIP. Both API do the same with different endpoints and parameters. I'm modeling classes as strategy pattern and the problem that i have encountered is the parameters of each method strategy because are different.
public interface VoIPRequests
{
string ApiKey { get; set; }
string GetExtensionsList();
string TriggerCall();
string DropCall();
string RedirectCall();
}
How can i change parameters for each of this methods depend on the implementation?.
It's good idea use strategy pattern for this case?
There is another pattern that suits better?
Thank you.
Per comment thread:
TriggerCall(), one api only needs one parameter "To" , and other api has two mandatory parameters "extension" and "destination"
I'll focus on TriggerCall, then, and let you extrapolate from there.
Implementation 1
public class VoIPRequests1 : VoIPRequests
{
private readonly object to; // Give this a more appropriate type
public VoIPRequests1(object to)
{
this.to = to;
}
public string TriggerCall()
{
// Use this.to here and return string;
}
// Other interface members go here...
}
Implementation 2
public class VoIPRequests2 : VoIPRequests
{
private readonly object extension; // Give this a more appropriate type
private readonly object destination; // Give this a more appropriate type
public VoIPRequests2(object extension, object destination)
{
this.extension = extension;
this.destination = destination;
}
public string TriggerCall()
{
// Use this.extension and this.destination here and return string;
}
// Other interface members go here...
}

WebApi method is unable to bind to model

Basically I'm trying to create a method in my webapi controller:
The method looks like this(the method body is relevant):
[HttpPost]
public HttpResponseMessage CpaLead([FromBody]CpaLeadVM model)
{
Here's the class declaration of the object being passed:
public class CpaLeadVM
{
public string UserIp = "";
public string UserCountry = "";
public double Earn = 0.0;
public string SurveyType = "";
}
The thing is; when I send a post request to the method, the model is always null.
The post request has the following data:
UserIp=hello
Earn=44.4
UserCountry=denmark
SurveyType=free
Shouldn't it be able to bind to the model or am I missing something here?
The problem is the "properties" you are trying to bind to are fields and not actual properties. The model binders and formatters in Web Api doesn't look at fields. If you change your model to:
public class CpaLeadVM
{
public CpaLeadVm()
{
UserIp = "";
UserCountry = "";
Earn = 0.0;
SurveyType = "";
}
public string UserIp {get;set;}
public string UserCountry {get;set;}
public double Earn {get;set;}
public string SurveyType {get;set;}
}
Your binding will work. As a side note, the [FromBody] attribute on your action is redundant since non-primitive values are bound from the request body by default.
As you may know, you can only get a single value from the body, which must be sent as "=value". See this article for more info
http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/
I'm not sure, but I think you could create your own model binder, which parses the body into your class. Another approach is to use JSON. Read more about that here
ASP.NET MVC 4 Post FromBody Not Binding From JSON

DataContract classes uninitialized at client side

I have the following class I'd like to send from my WCF (C#) service to my client (WPF):
[DataContract]
public class OutputAvailableEventArgs
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Message { get; private set; }
[DataMember]
public bool IsError { get; private set; }
public OutputAvailableEventArgs(int id) : this(id, false, "") { }
public OutputAvailableEventArgs(int id, string output) : this(id, false, output) { }
public OutputAvailableEventArgs(int id, bool isError, string output)
{
ID = id;
IsError = isError;
Message = output;
}
}
It's used by the service as follows:
var channel = OperationContext.Current.GetCallbackChannel<IClientCallback>();
channel.OutputAvailable(new OutputAvailableEventArgs(1, false, "some message"));
At the client side, the members get their default values.
I tried marking them with IsRequired attribute but now the OutputAvailable at the client is not called. The code at the service side seems to run smoothly (I didn't notice anything with the debugger).
How can I transfer a DataContract class with WCF while maintaining the members' values?
(I saw solutions that suggested to use OnSerialized and OnDeserialized but I don't need just a default constructor.)
I saw many different solutions for this problem. For other people's sake I'll write some of them down + what worked for me:
It seems that in some cases specifying the items' order solves the problem. Please see this SO question for full details.
If it's some default initialization you're after, you can use OnSerialized and OnDeserialized methods to call your initialization methods.
I also tried using the IsRequired attribute on my DataMembers but still didn't get my objects.
What worked for me was adding NameSpace property in the DataContract attribute. Apparently, In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides.

access wcf interface method from client

This is one of the classes in Interface file.
[DataContract]
public class ClassX
{
public ClassX()
{
ClassXParameters = new List<ClassXParameter>();
}
public void Add(string name, string value)
{
ClassXParameters.Add(new ClassXParameter() { Name = name, Value = value });
}
[DataMember]
public List<ClassXParameter> ClassXParameters { get; set; }
}
[DataContract]
public class ClassXParameter
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
}
on the client I'm trying to do something like this
ClassX classx = new ClassX();
classx.Add("testname", "testvalue");
But this .Add method is not even visible.
currently I'm doing
ClassX classx = new ClassX();
List<ClassXParameter> params = new List<ClassXParameter()>;
params.add(new ClassXParameter() {Name="testname", Value="testvalue"});
classx.ClassXParameters = params;
Is there anyway I can do what I'm trying to do?
Note: I am not sure why some of the text above are in bold.
If you autogenerate the client code from scratch, it will generate a new class, which contains those members and properties that are marked with DataContract.
If you have methods that you want available on the client, you can accomplish this by putting the DataContract types in an own assembly, which you reference from both the server and the client. When you generate the service reference you have to choose the option to reuse existing classes instead of generating new ones.
Often it is suitable to put data validation rules in the data contract classes property setters. Reusing the data contract assembly in the client will cause the data validation to occur directly on the client, without the need for a roundtrip. It also causes the error in a place where it is much easier to spot than if it is reported as deserialization error.
Data Contracts are for data only. Any methods will not be visible on the client.
The bold was because of the "-----".

WCF serialization and Value object pattern in Domain Driven Design

The book Domain Driven Design by Eric Evans describes pattern called value object. One of the important characteristics of a value object is that it is immutable.
As an example I have a value object "Clinic" which must have a name and an id. To make it a value object I do not provide setters on name and id. Also to make sure that there is not invalid instance I take name and id in a constructor and do not provide at parameter less constructor.
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{get; private set;}
public string Id{get; private set;}
}
The problem is that when I try to return this object from a WCF Service I get an exception that the object does not have parameter less constructor and the properties do not have public setters. I want to avoid adding parameter less constructor and public setters because then my domain model pattern goes for a toss. How can I get around this problem?
Regards,
Unmesh
I had a similar problem with serializing immutable types before, in the end I decided to implement the ISerializable interface and use the SerializationInfo to store & retrieve the private variables at both ends of the serialization/deserialization process:
http://theburningmonk.com/2010/04/net-tips-making-a-serializable-immutable-struct/
I just built and run a test app using the same technique and it seems to work for me. So in terms of changes to your Clinic class you could change it to:
[Serializable]
public class Clinic : ISerializable
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public Clinic(SerializationInfo info, StreamingContext context)
{
Name= info.GetString("Name");
Id= info.GetString("Id");
}
public string Name{get; private set;}
public string Id{get; private set;}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", Name);
info.AddValue("Id", Id);
}
}
This will solve the problem you're having passing the data back from WCF. But from a design point of view, I agree with what Ladislav is saying and typically you will want to separate your domain objects with objects purely intended for message passing (DataTransferObjects), and in that case here's an example of how you MIGHT approach it:
// the domain object (NOT EXPOSED through the WCF service)
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{ get; private set;}
public string Id{ get; private set;}
// other methods encapsulating some business logic, etc.
...
}
// the corresponding DTO object for the domain object Clinic
// this is the type exposed through the WCF layer, that the client knows about
[DataContract]
public class ClinicDTO
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Id { get; set; }
}
// WCF service contract, NOTE it returns ClinicDTO instead of Clinic
[ServiceContract]
public interface IClinicService
{
[OperationContract]
ClinicDTO GetClinicById(string id);
}
To ease the pain of converting from Clinic to ClinicDTO, you could either add a method on Clinic to do this or implement an implicit/explicit converter. I've got an example on how to do this here:
http://theburningmonk.com/2010/02/controlling-type-conversion-in-c/
Hope this helps!
The problem is that your value object is not serializable. How do you plan to use the service? Do you plan to share domain objects / value objects with your clients? If yes than it IMO violates your domain driven desing - only business layer should be able to work with domain objects and call their methods. If you don't want to share objects you will probably create proxy by add service reference which will generate data contrats for the client. These contrats will have public parameterless constructor and all properties settable (and no domain methods).
If you want to have real Domain driven design you should not expose your domain objects in WCF. Instead you should create set of DTO and expose those DTOs. Service layer will be responsible of converting those DTOs to Domain objects / value objects and vice-versa.