We are using Praxedo for multiple services, many of which are on different SOAP APIs which are never-the-less similar to instantiate and consume.
Rather than repeating code, I'd like to make a generic way to do this.
I've created this factory class:
using PraxedoIntegration.Common.Configurations;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using WcfCoreMtomEncoder;
namespace PraxedoV6.ManagerClientWrappers.Helpers
{
public class PraxedoClientFactory
{
private readonly PraxedoSettings _praxedoSettings;
public PraxedoClientFactory(
PraxedoSettings praxedoSettings
) =>
_praxedoSettings = praxedoSettings ?? throw new ArgumentNullException(nameof(praxedoSettings));
public T Create<T, TChannel>(Uri endpoint, HttpsTransportBindingElement bindingElement)
where T : ClientBase<TChannel>, new()
where TChannel : class
{
MtomMessageEncoderBindingElement encoding = CreateMtomMessageEncoderBindingElement();
CustomBinding customBinding = new(encoding, bindingElement);
T managerClient = (T)Activator.CreateInstance(typeof(T), new object[] { customBinding, endpoint });
_praxedoSettings.AddAuthorizationTo(managerClient);
return Authorize<T, TChannel>(managerClient);
}
private static MtomMessageEncoderBindingElement CreateMtomMessageEncoderBindingElement() =>
new(new TextMessageEncodingBindingElement
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
});
public T Authorize<T, TChannel>(T managerClient)
where T : ClientBase<TChannel>, new()
where TChannel : class
{
_ = new OperationContextScope(managerClient.InnerChannel);
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name]
= _praxedoSettings.ToHttpRequestMessageProperty();
return managerClient;
}
}
}
It compiles just fine and dandy.
But when I try to consume it, like:
_customerManagerClient = praxedoClientFactory.Create<CustomerManagerClient, CustomerManagerV6.CustomerManager>(
_praxedoSettings.CustomerManagerEndpoint,
new HttpsTransportBindingElement()
I get runtime errors like:
WorkflowTests.TaskProcessor.NewOrg.CreateCustomerAndLocationActionDiagnosticTool.CreateCustomerAndLocationInPraxedoTest Source: CreateCustomerAndLocationActionDiagnosticTool.cs line 15
Duration: 23 msMessage: System.MissingMethodException : Constructor on type 'CustomerManagerV6.CustomerManagerClient' not found.
Stack Trace: RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) Activator.CreateInstance(Type type, Object[] args) PraxedoClientFactory.Create[T,TChannel](Uri endpoint, HttpsTransportBindingElement bindingElement) line 25 CustomerManagerClientWrapper.ctor(PraxedoClientFactory praxedoClientFactory, PraxedoSettings praxedoSettings, PraxedoThrottleOut praxedoThrottleOut) line 30 CreateCustomerAndLocationActionDiagnosticTool.CreateCustomerAndLocationInPraxedoTest() line 22 --- End of stack trace from previous location ---
In this instance CustomerManagerClient
is a class which inherits from the abstract System.ServiceModel.ClientBase<CustomerManagerV6.CustomerManager>
and implements the interface CustomerManagerV6.CustomerManager
(all per code autogenerated from the WSDL).
In another instance we have BusinessEventManagerClient
which (you might predict) inherits from the abstract System.ServiceModel.ClientBase<BusinessEventManagerV6.BusinessEventManagerClient>
and implements the interface BusinessEventManagerV6.BusinessEventManagerClient
(all [again] per code autogenerated from the WSDL).
And we have about half a dozen other "manager clients" which are similar.
Am I declaring the Create()
method with the correct type parameters and constraints?
Am I invoking it correctly?
Is there something else I should do?
Am I trying to do something impossible?
CodePudding user response:
Figured out the problem.... I was forgetting to convert my Uri endpoint
to an EndpointAddress
, like so:
public T Create<T, TChannel>(Uri endpoint, HttpsTransportBindingElement bindingElement)
where T : ClientBase<TChannel>, TChannel, new()
where TChannel : class
{
MtomMessageEncoderBindingElement encoding = CreateMtomMessageEncoderBindingElement();
CustomBinding customBinding = new(encoding, bindingElement);
EndpointAddress endpointAddress = new(endpoint);
T managerClient = (T)Activator.CreateInstance(typeof(T), new object[] { customBinding, endpointAddress });
_praxedoSettings.AddAuthorizationTo(managerClient);
return Authorize<T, TChannel>(managerClient);
}
... so the constructor was getting the wrong type of object.