Write a helper class to invoke WCF services in asp.net core

◇◆丶佛笑我妖孽 提交于 2019-12-24 21:08:13

问题


I wrote a client for WCF service that I want to consume in my asp.net core service:

public class ExternalCompanyClient : IExternalCompanyClient
{
    public async Task<string> CallAsync() 
    {
        BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.None);
        EndpointAddress endpointAddress = new EndpointAddress(new Uri("http://........."));
        ChannelFactory<IExternalCompanyService> factory = new ChannelFactory<IExternalCompanyService>(basicHttpBinding, endpointAddress);
        IExternalCompanyService serviceProxy = factory.CreateChannel();

        ((ICommunicationObject)serviceProxy).Open();
        var opContext = new OperationContext((IClientChannel)serviceProxy);
        var prevOpContext = OperationContext.Current; 
        OperationContext.Current = opContext;

        ExternalCompanyResponse ret = null;

        var request = GetRequest();

        try
        {
            ret = await serviceProxy.GetCompanyInfoAsync(request);

            factory.Close();
            ((ICommunicationObject)serviceProxy).Close();
        }
        catch (MessageSecurityException ex)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw;
        }
        finally
        {
            WCFHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory);
            OperationContext.Current = prevOpContext;
        }

        return ret;
    }
} 

In the above code IExternalCompanyService is an interface that was automatically generated when I added WCF service to the project.

So, now I can call this client and it works fine.

Now what I want to do is to move all infrastructure code into separate ServiceCaller class and use this class for all other WCF clients for the usability purposes.

And this is my unsuccessful attempt (I added the comments to see the differences in CallAsync method).

internal class ServiceCaller<TInterface, TIn, TOut> where TInterface : IService<TIn, TOut>
{
    private readonly Uri _uri;

    public ServiceCaller(Uri uri)
    {
        var attributes = typeof(TInterface).GetCustomAttributes(typeof(ServiceContractAttribute), true);

        if (attributes.Length == 0)
        {
            throw new ArgumentException($"Interface {typeof(TInterface).Name} does not have ServiceContract attribute");
        }

        _uri = uri;
    }

    public async Task<TOut> CallAsync(TIn request, TOut valueIfError = default(TOut))
    {
        BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.None);   
        EndpointAddress endpointAddress = new EndpointAddress(_uri);  // uri is passed into via a constructor
        ChannelFactory<TInterface> factory = new ChannelFactory<TInterface>(basicHttpBinding, endpointAddress);
        TInterface serviceProxy = factory.CreateChannel();  // now the serviceProxy is of type of TInterface

        ((ICommunicationObject)serviceProxy).Open();
        var opContext = new OperationContext((IClientChannel)serviceProxy);
        var prevOpContext = OperationContext.Current;
        OperationContext.Current = opContext;

        TOut ret = valueIfError;   // the return has TOut type now

        try
        {
            ret = await serviceProxy.InvokeAsync(request);   // and here I invoke the interface method

            factory.Close();
            ((ICommunicationObject)serviceProxy).Close();
        }
        catch (MessageSecurityException ex)
        {
            throw;
        }
        catch (Exception ex)
        {
            throw;
        }
        finally
        {             
            CloseCommunicationObjects.CloseOrAbort((ICommunicationObject)serviceProxy, factory);
            OperationContext.Current = prevOpContext;
        }

        return ret;
    }
}

So, I added IService interface:

public interface IService<TIn, TOut>
{
    Task<TOut> InvokeAsync(TIn request);
}

And, finally, my new simplified ExternalCompanyClient:

public class ExternalCompanyClient : IExternalCompanyClient, IService<ExternalCompanyRequest, ExternalCompanyResponse>
{
    private readonly IExternalCompanyService _externalCompanyService;

    public ExternalCompanyClient(IExternalCompanyService externalCompanyService)
    {
        _externalCompanyService = externalCompanyService;
    }

    private ExternalCompanyRequest GetRequest() =>  // some logic goes here   

    public async Task<string> GetCompanyInfoAsync()
    {
        var request = GetRequest();

        // here I get a compilation error: 
        // The type 'IExternalCompanyService' cannot be used as type parameter 'TInterface' in the generic type
        // or method 'ServiceCaller<TInterface, TIn, TOut>'. There is no implicit reference conversion from
        // 'IExternalCompanyService' to 'IService<ExternalCompanyRequest, ExternalCompanyResponse>'
        var serviceCaller = new ServiceCaller<IExternalCompanyService, ExternalCompanyRequest, ExternalCompanyResponse>(
            new System.Uri("http://......................."));


        var result = await serviceCaller.CallAsync(request);

        return result;
    }

    public Task<ExternalCompanyResponse> InvokeAsync(ExternalCompanyRequest request)
    {
        return _externalCompanyService.GetCompanyInfoAsync(request);
    }
}

I should somehow enrich autogenerated IExternalCompanyService in such a way that it has to have InvokeAsync method without making changes in autogenerated file.

来源:https://stackoverflow.com/questions/57976249/write-a-helper-class-to-invoke-wcf-services-in-asp-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!