0

EDIT - Now working with WCF Client proxy creation moved out of DI and into the CustomerUpdateProcessor

  using (OperationContextScope scope = new OperationContextScope(starClient.InnerChannel))
                {
                    AddHeaders(appSettings, fordHeader, dcs, token);
                    var message = new ProcessMessage()
                    {

                        payload = customerPayload
                    };
                    task = starClient.ProcessMessageAsync(null, message);
                }
                var result = await task;
                return result.ProcessMessageResponse.payload;

Still would like to get it working with DI if anyone can recommend a way to handle

I have a .NET5.0 app. which uses hangfire to fire recurring jobs

Each recurring job creates a WCF client proxy to connect to a SOAP service and ProcessMessages.

The issue im facing is that when the WCF proxy is created its getting stuck in a faulted state and therefore can no longer be used.. i have tried calling _client.Abort() but then all that happens is the error message changes to channel has been aborted

I am using microsoft.extensions.dependencyinjection to register all my services, including the client proxy as transient in the container as follows:

Service Registration

public static IServiceCollection AddCustomerUpdate(this IServiceCollection services, IConfigurationRoot config)
    {
        services.AddTransient<ICustomerUpdateService, CustomerUpdateService>();
        services.AddTransient<ICustomerUpdateProcessor, CustomerUpdateProcessor>();
        services.AddTransient<IDmsCustomerUpdateMessageService, DmsCustomerUpdateMessageService>();
        services.AddCustomerUpdateClient(config);
        return services;
    }


public static IServiceCollection AddCustomerUpdateClient(this IServiceCollection services, IConfigurationRoot config)
    {
        ServicePointManager.Expect100Continue = true;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
                | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
        BasicHttpsBinding binding = new BasicHttpsBinding()
        {
            SendTimeout = TimeSpan.FromMinutes(1),
            OpenTimeout = TimeSpan.FromMinutes(1),
            CloseTimeout = TimeSpan.FromMinutes(1),
            ReceiveTimeout = TimeSpan.FromMinutes(10),
            
            Namespace = "http://www.starstandards.org/webservices/2005/10/transport",
            //HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
            TextEncoding = System.Text.Encoding.UTF8,
            //MessageEncoding = WSMessageEncoding.Text,
            UseDefaultWebProxy = true,
            
        };
        binding.MaxReceivedMessageSize = 50000000;
        binding.ReaderQuotas.MaxArrayLength = 50000000;
        binding.ReaderQuotas.MaxStringContentLength = 50000000;
        binding.ReaderQuotas.MaxNameTableCharCount = 50000000;
        var endpointConfig = config.GetSection(nameof(EndPointConfig));
        EndpointAddress endpointAddress = new EndpointAddress($"{endpointConfig[nameof(EndPointConfig.CuUrl)]}");
        
        var starClient = new CustomerUpdateStarTransportPortTypesClient(binding, endpointAddress);
        services.AddTransient(x => starClient);
        return services;
    }

Service Execution

Then on each execution of the hangfire RecurringJob it invokes the ICustomerUpdateProccesor and loops a list of customers processing the updates for each one. for this a single instance of the WCF client proxy should be being used

public class CustomerUpdateProcessor : ICustomerUpdateProcessor
{
    private readonly ICustomerRepository _customerRepository;
    private readonly ICustomerIntegrationRepository _customerIntergrationRepository;
    private readonly IDmsCustomerUpdateMessageService _dmsCustomerUpdateMessageService;
    private readonly ICustomerUpdateService _customerUpdateService;
    private readonly IFordOauthService _authService;
    private readonly IIntegrationRepository _integrationRepository;

    public CustomerUpdateProcessor(
        ICustomerIntegrationRepository customerIntegrationRepository, 
        ICustomerRepository customerRepository,
        IDmsCustomerUpdateMessageService dmsCustomerUpdateMessageService,
        ICustomerUpdateService customerUpdateService,
        IFordOauthService authService,
        IIntegrationRepository integrationRepository
        )
    {
        _authService = authService;
        _customerIntergrationRepository = customerIntegrationRepository;
        _integrationRepository = integrationRepository;
        _customerRepository = customerRepository;
        _dmsCustomerUpdateMessageService= dmsCustomerUpdateMessageService;
        _customerUpdateService = customerUpdateService;
    }

    [DisableConcurrentExecution(3600)]
    [AutomaticRetry(Attempts = 0)]
    public async Task ProcessUpdatesForActiveCustomersAsync()
    {
        var customers = _customerRepository.GetAllCustomers();

        foreach (var customer in customers)
        {
            var integrations = _customerIntergrationRepository.GetIntegrationsForCustomer(customer.Id);
            
            foreach (var integration in integrations)
            {
                if (integration.Name == "Xfi.CustomerUpdate" && integration.Enabled)
                {
                    var integrationParams = integration.Paramaters;
                   
                    var dealerCode = integrationParams.Single(x => x.Name == "DealerCode").Value;
                    var appArea = new ApplicationAreaModel()
                    {
                        DealerCode = dealerCode
                    };
                    var messages = await _dmsCustomerUpdateMessageService.GetMessagesAsync(customer.ServerAddress, appArea);
                    if (messages != null)
                    {
                        foreach (var message in messages)
                        {
                            var cuMessage = message as CustomerUpdateMessage;
                            var builder = new ProcessCustomerInformationBuilder();
                            builder.AddApplicationArea(cuMessage.ApplicationArea);
                            builder.AddHeader();
                            if (cuMessage.CustomerModel is IndividualCustomerModel)
                            {
                                builder.AddIndivualCustomer(cuMessage.CustomerModel as IndividualCustomerModel);

                            }
                            else
                            {
                                builder.AddSpecifiedOrganization(cuMessage.CustomerModel as BusinessCustomerModel);
                            }
                            var payload = builder.BuildPayload();
                            var token = await GetAccessTokenAsync(integrationId: integration.Id);
                            var appsettings = new ApplicationSetttings()
                            {
                                XmotiveAppId = ""
                            };
                            await _customerUpdateService.ProcessCustomerUpdateAsync(payload, dealerCode, appsettings, token.ToString(), customer.Name);
                        }      
                    }
                }
            }
        }
    }

Soap Service

In this Processor a ICustomerUpdateService is used to invoke the WCF client proxy and call the SOAP service.

 public class CustomerUpdateService : StarService, ICustomerUpdateService
{
    private readonly CustomerUpdateStarTransportPortTypesClient _starClient;
    private readonly IServiceProvider _serviceProvider;

    public CustomerUpdateService(CustomerUpdateStarTransportPortTypesClient starClient, IServiceProvider serviceProvider)
    {
        _starClient = starClient;
        _serviceProvider = serviceProvider;
    }

    [OperationContract]
    public async Task<AcknowledgeCustomerInformationPayload> ProcessCustomerUpdateAsync(
        ProcessCustomerInformationPayload customerPayload,
        string siteCode,
        ApplicationSetttings appSettings,
        string token,
        string customerName
        )
    {

        var fordHeader = new FordDealerIdentity()
        {
            SiteCode = siteCode
        };

        DataContractSerializer dcs = new DataContractSerializer(typeof(FordDealerIdentity));
        Task<ProcessMessageResponse1> task;

        using (OperationContextScope scope = new OperationContextScope(_starClient.InnerChannel))
        {
            AddHeaders(appSettings, fordHeader, dcs, token);
            var message = new ProcessMessage()
            {

                payload = customerPayload
            };
            task = _starClient.ProcessMessageAsync(null, message);
        }
        var result = await task;
        return result.ProcessMessageResponse.payload;
    }
}

The error throws on this line using (OperationContextScope scope = new OperationContextScope(_starClient.InnerChannel))

It throws the error "Object cannot be used as its in a faulted state"

It appears the issue is around multithreading and the WCF client proxy, but im not sure how to correctly handle and im hitting barriers with missing functionality in .NETCores port of WCF and System.ServiceModel.Primitives

CT1971
  • 13
  • 1
  • 7
  • Maybe you can find a solution from this link: https://stackoverflow.com/questions/9409329/wcf-cannot-be-used-for-communication-because-it-is-in-the-faulted-state – Lan Huang Nov 03 '21 at 08:58
  • Managed to resolve my issue by moving the WCF client proxy creation out of DI and into the service itself wrapped in a using Updated the question – CT1971 Nov 03 '21 at 16:24

1 Answers1

0

Maybe you can try try...catch instead of using statement

Lan Huang
  • 613
  • 2
  • 5