Home > Enterprise >  In C#, is it possible to create a generic method for instantiating multiple "manager clients&qu
In C#, is it possible to create a generic method for instantiating multiple "manager clients&qu

Time:10-05

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 ms

Message: 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.

  • Related