Background
I have a third party system that offers SOAP APIs. The class structure of their WSDLs leaves a lot to be desired in general, but one area I'm struggling with is streamlining the retrieval of bindings. Their sample code has around 20 of these methods, one for each endpoint/class/interface:
public static ICreateObjectPortType GetCreateObjectHttpBinding()
{
var servicePort = new CreateObjectPortTypeClient();
servicePort.Endpoint.ListenUri = GetServiceUrl("ReadObject");
servicePort.ClientCredentials.UserName.UserName = SDK.Username;
servicePort.ClientCredentials.UserName.Password = SDK.Password;
SetupHttpHeader(servicePort.InnerChannel);
return servicePort;
}
My primary goal is to convert these into a generic method that I can call with just a parameter or two and return the class. (You'll also see in my current code that I'm implementing some dynamic binding code. This is because I have multiple companies to access and their system structure requires bindings per company. This is not relevant to the question.)
The basic structure of all the classes and interfaces is as follows. I have included only the constructor I'm targeting. These are defined in the stub classes created from the WSDLs, and not something I want to directly edit if possible.:
Interfacespublic interface ICreateObjectPortType { }
Classes
public partial class CreateObjectPortTypeClient : ClientBase<ICreateObjectPortType>, ICreateObjectPortType
{
public CreateObjectPortTypeClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
}
Here are examples of the other interfaces and classes declared in the stubs. The only common thing between all of them is all of the classes inherit from ClientBase<interface> and interface:
IReadObjectPortTypeandReadObjectPortTypeClientIUpdateObjectPortTypeandUpdateObjectPortTypeClientIFindObjectsPortTypeandFindObjectsPortTypeClient
Here is the SetupHttpHeader method defined in their sample code. I haven't checked to see if it's actually needed yet, but I'm including it for completeness:
public static void SetupHttpHeader(IClientChannel serviceChannel)
{
using (new OperationContextScope(serviceChannel))
{
// Add a HTTP Header to an outgoing request
var requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers["Authorization"] = "Basic " + System.Convert.ToBase64String(Encoding.UTF8.GetBytes(SDK.Username + ":" + SDK.Password));
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
}
}
My Code
And lastly for code samples, here is my stab at a generic method, the supporting credential setting method, and what it looks like to use them:
public static T ConfigureServicePort<T>(Func<string,EndpointAddress, T> test, string serviceName) where T : class
{
var endpointName = serviceName + "HttpPort";
var endpointAddress = new EndpointAddress($"https://hcmiller.myprintdesk.com:443/rpc/company:public/services/{serviceName}");
var servicePort = test(endpointName, endpointAddress);
return servicePort;
}
public static void SetClientCredentials(this ClientCredentials creds)
{
creds.UserName.UserName = SDK.Username;
creds.UserName.Password = SDK.Password;
}
public static ICreateObjectPortType GetCreateObjectHttpBinding()
{
var servicePort = ConfigureServicePort((x, y) => new CreateObjectPortTypeClient(x, y), "CreateObject");
servicePort.ClientCredentials.SetClientCredentials();
SetupHttpHeader(servicePort.InnerChannel);
return servicePort;
}
As you can see, I'm not quite there yet. I have successfully implemented a function parameter in the method in order to call a constructor on T with parameters. It's far more open than I prefer my generics to be, but I don't know of another way to allow this while also restricting what T can be.
The Question
My problem right now is getting the setting of ClientCredentials and calling of SetupHttpHeader to happen in that one generic method as well. Given that there is no base interface for the classes, and the only common ground they have is ClientBase<T> I haven't found a way to treat servicePort inside ConfigureServicePort as something that inherits from ClientBase<T> to get at the necessary properties.
How can I get it so that ConfigureServicePort<T> can treat its T like a ClientBase<T> so that I can get to the properties I need to configure?