I'm using a 3rd party web service where all it's methods have the following signature:
SomeResponseClass SomeMethod(SomeRequestClass request)
Every method has it's own request class and it's own response class, However there are a few things that all requests have in common, and all responses have in common.
Every request class has a couple of properties for identification: username and token, and every response class have a couple of properties to indicate if there was an error: status and error.
I'm looking for an elegant way to create a method that will instantiate my requests - so that I will not have to repeat myself in every method calling the service. Since I can't change the classes, and there is no common interface or base class to all of them, the only solution I found was to use dynamic:
private T CreateRequest<T>() where T : new()
{
if(string.IsNullOrEmpty(_Token))
{
Login();
}
// this has to be dynamic for the next 2 rows to pass compilation.
dynamic request = new T();
request.username = _UserName;
request.token = _Token;
return (T)request;
}
And something similar to populate my own entities from the service responses:
// response have to be dynamic for getting the error and status properties
private static T CreateServiceResponse<T>(dynamic response) where T : ServiceResponse, new()
{
T result = new T();
result.Error = (response == null) ? "Service response is null" : response.error;
result.Status = (response == null) ? -1 : response.status;
return result;
}
(This method is used inside every method that I use to populate my entities (all derived from ServiceResponse) with the data from the service response.)
Is there any way to do something like this without using dynamic?
To me, it kinda feels like using a dirty hack...
If I could somehow "inject" a common interface for all requests to implement and a common interface for all responses to implement I could use simple Generics, but I guess that's impossible, or is it?
When you are using service references created by visual studio, all generated classes are marked as partial. That means you can extend them with your own code. In your case that would be an interface that provides the repetitive properties. You can use that interface as a generic constraint.
You first create the common interface. To make things easy, you use the same name and type for your common properties as defined in the service reference:
interface IRequest {
string username { get; set; }
string token { get; set; }
}
You then create a code file and in the same namespace as the service reference you define a partial class for each service request class (with the same name). Each of these classes implements your interface:
partial class SomeRequestClass : IRequest {}
Having chosen the same property names and types, you don‘t have to specifically implement the interface.
Finally you use generic constraints:
private T CreateRequest<T>() where T : IRequest, new()
{
if(string.IsNullOrEmpty(_Token))
{
Login();
}
T request = new T();
request.username = _UserName;
request.token = _Token;
return request;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With