ramaz bluashvili
ramaz bluashvili

Reputation: 11

How to consume WCF remote service in .net 6 with service reference

Okay, my head is dizzy because I have spent past week looking for a solution or a decent explanation.

Here is my not so hard goal: I just need to take remotely hosted SOAP service (Created long time ago in a galaxy far, far away), connect it to my class library, which will then be used by ASP.NET WebApi.

Did not seem very hard at first, Except, I can not get any response from this service, no matter how I try.

In usual cases it returns null, but when I comment a DebuggerStepThroughAttribute on service implementation and place a breakpoint inside of any method, an exception is fired: System.ServiceModel.CommunicationObjectFaultedException. Channel and InnerChannel throw this exception and I can not figure out how to get rid of it. I get the feeling that this entire client does not send the needed requests to the remote service. I have a Postman request built and functional, which sends regular POST request with xml body and receives a response, so remote service is not faulted.

I followed every instruction which I could find, even samples on Github for CoreWCF, I was still not able to figure this out. My entire code is generated by svcutil, dependency injection part and options part was written by me. I will attach all the files:

connectedServices.json file, generated by VS2022:

{
  "ExtendedData": {
    "inputs": [
      "https://services.rs.ge/WayBillService/WayBillService.asmx"
    ],
    "collectionTypes": [
      "System.Array",
      "System.Collections.Generic.Dictionary`2"
    ],
    "namespaceMappings": [
      "*, RevenueService"
    ],
    "targetFramework": "net6.0",
    "typeReuseMode": "All"
  }
}

Also generated Channel interface (I have no idea what this is or how to use it):

[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
public interface WayBillsSoapChannel : RevenueService.WayBillsSoap, System.ServiceModel.IClientChannel
{
}

ServiceContract with one of the methods:

  [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName="RevenueService.WayBillsSoap")]
    public interface WayBillsSoap
    {
        [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/get_error_codes", ReplyAction="*")]
        System.Threading.Tasks.Task<RevenueService.get_error_codesResponse> get_error_codesAsync(RevenueService.get_error_codesRequest request);
    }

Implementation of service contract:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.3")]
public partial class WayBillsSoapClient : System.ServiceModel.ClientBase<RevenueService.WayBillsSoap>, RevenueService.WayBillsSoap
{
    
    /// <summary>
    /// Implement this partial method to configure the service endpoint.
    /// </summary>
    /// <param name="serviceEndpoint">The endpoint to configure</param>
    /// <param name="clientCredentials">The client credentials</param>
    static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
    
    public WayBillsSoapClient(EndpointConfiguration endpointConfiguration) : 
            base(WayBillsSoapClient.GetBindingForEndpoint(endpointConfiguration), WayBillsSoapClient.GetEndpointAddress(endpointConfiguration))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public WayBillsSoapClient(EndpointConfiguration endpointConfiguration, string remoteAddress) : 
            base(WayBillsSoapClient.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress))
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public WayBillsSoapClient(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(WayBillsSoapClient.GetBindingForEndpoint(endpointConfiguration), remoteAddress)
    {
        this.Endpoint.Name = endpointConfiguration.ToString();
        ConfigureEndpoint(this.Endpoint, this.ClientCredentials);
    }
    
    public WayBillsSoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress)
    {
    }
            
    public virtual System.Threading.Tasks.Task OpenAsync()
    {
        return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen));
    }
    
    public virtual System.Threading.Tasks.Task CloseAsync()
    {
        return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginClose(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndClose));
    }
    
    private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WayBillsSoap))
        {
            System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
            result.MaxBufferSize = int.MaxValue;
            result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
            result.MaxReceivedMessageSize = int.MaxValue;
            result.AllowCookies = true;
            result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
            return result;
        }
        if ((endpointConfiguration == EndpointConfiguration.WayBillsSoap12))
        {
            System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
            System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
            textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.None);
            result.Elements.Add(textBindingElement);
            System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement();
            httpsBindingElement.AllowCookies = true;
            httpsBindingElement.MaxBufferSize = int.MaxValue;
            httpsBindingElement.MaxReceivedMessageSize = int.MaxValue;
            result.Elements.Add(httpsBindingElement);
            return result;
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }
    
    private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.WayBillsSoap))
        {
            return new System.ServiceModel.EndpointAddress("https://services.rs.ge/WayBillService/WayBillService.asmx");
        }
        if ((endpointConfiguration == EndpointConfiguration.WayBillsSoap12))
        {
            return new System.ServiceModel.EndpointAddress("https://services.rs.ge/WayBillService/WayBillService.asmx");
        }
        throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
    }
    
    public enum EndpointConfiguration
    {
        
        WayBillsSoap,
        
        WayBillsSoap12,
    }
}

Options class:

public class RevenueServiceOptions
{
    public string? ServiceUrl { get; set; }

    public string? ServiceUser { get; init; }

    public string? ServicePassword { get; init; }

    internal void Validate()
    {
        if (string.IsNullOrWhiteSpace(ServiceUrl))
            throw new Exception($"{nameof(RevenueServiceOptions)}.{nameof(ServiceUrl)} should not be empty");

        if (string.IsNullOrWhiteSpace(ServiceUser))
            throw new Exception($"{nameof(RevenueServiceOptions)}.{nameof(ServiceUser)} should not be empty");

        if (string.IsNullOrWhiteSpace(ServicePassword))
            throw new Exception($"{nameof(RevenueServiceOptions)}.{nameof(ServicePassword)} should not be empty");
    }
}

And DependencyInjection logic:

public static class DependencyInjection
{
    public static void AddExternalServices(this IServiceCollection services, RevenueServiceOptions revenueServiceOptions)
    {
        services.AddRevenueServiceOptions(revenueServiceOptions);

        services.AddServices(revenueServiceOptions);

        services.ConfigureAutoMapper();
    }

    private static void AddRevenueServiceOptions(this IServiceCollection services, RevenueServiceOptions revenueServiceOptions)
    {
        if (revenueServiceOptions == null)
            throw new ArgumentNullException(nameof(revenueServiceOptions));

        revenueServiceOptions.Validate();

        services.AddSingleton(revenueServiceOptions);
    }

    private static void AddServices(this IServiceCollection services, RevenueServiceOptions revenueServiceOptions)
    {
        services.AddScoped<WayBillsSoap>(_ =>
            new WayBillsSoapClient(WayBillsSoapClient.EndpointConfiguration.WayBillsSoap, revenueServiceOptions.ServiceUrl));
    }

    private static void ConfigureAutoMapper(this IServiceCollection services)
    {
        var mappingConfig = new MapperConfiguration(mc =>
        {
            mc.AddProfile(new AutoMapper());
        });

        var mapper = mappingConfig.CreateMapper();
        services.AddSingleton(mapper);
    }
}

serviceUrl, serviceUser and servicePassword is taken from appsettings.json file

Could you please tell me, am I doing something wrong? does this client need additional configuration to send POST requests? Or could you, may be, give me an advise about a good resource which I was not able to find and which will actually help?

Thank you.

P.S. While here, could you also tell me how or where to use ConfigureEndpoint partial method?

Upvotes: 0

Views: 3503

Answers (0)

Related Questions