Make WebService Calls in ASP.NET CORE

Moving to ASP.NET Core is fun and interesting but then you begin to fall into problems trying to do some stuffs you use to do easily in ASP.NET MVC.

One of such is trying to make a call to a webservice but then where is the WEB.CONFIG xml file that keeps webservices configurations ? It is no more in ASP.NET CORE what we have now is the APPSETTINGS which is a json file. Hmmmmm……………..

In this post i will show how you can make webservice calls easily.

In order to use a webservice , i went online to get a free webservice from http://www.dneonline.com/calculator.asmx , this service has 4 simple operations which are Add , Divide , Multiply , Substract.

In this demo i will use the ADD method/operation which basically adds 2 numbers together. With this sample in this tutorial you can basically call any webservice after going through this tutorial.

The first thing we are going to do is a generate a proxy class for the webservice in our code using the in-built SVCUTIL.EXE command in the Visual Studio Command Prompt

Svc

This will automatically generate a proxy class and output.xml file but in this case we don’t need the output.xml in our code.

Svc Generated

Simply copy this generated .cs file into your code. For this service this is the generated .cs code. Note this is auto-generated by the VS Command Prompt using command above.

//------------------------------------------------------------------------------
// 
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------



[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="CalculatorSoap")]
public interface CalculatorSoap
{
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Add", ReplyAction="*")]
    int Add(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Add", ReplyAction="*")]
    System.Threading.Tasks.Task<int> AddAsync(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Subtract", ReplyAction="*")]
    int Subtract(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Subtract", ReplyAction="*")]
    System.Threading.Tasks.Task<int> SubtractAsync(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Multiply", ReplyAction="*")]
    int Multiply(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Multiply", ReplyAction="*")]
    System.Threading.Tasks.Task<int> MultiplyAsync(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Divide", ReplyAction="*")]
    int Divide(int intA, int intB);
    
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/Divide", ReplyAction="*")]
    System.Threading.Tasks.Task<int> DivideAsync(int intA, int intB);
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface CalculatorSoapChannel : CalculatorSoap, System.ServiceModel.IClientChannel
{
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class CalculatorSoapClient : System.ServiceModel.ClientBase<CalculatorSoap>, CalculatorSoap
{
    
    public CalculatorSoapClient()
    {
    }
    
    public CalculatorSoapClient(string endpointConfigurationName) : 
            base(endpointConfigurationName)
    {
    }
    
    public CalculatorSoapClient(string endpointConfigurationName, string remoteAddress) : 
            base(endpointConfigurationName, remoteAddress)
    {
    }
    
    public CalculatorSoapClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(endpointConfigurationName, remoteAddress)
    {
    }
    
    public CalculatorSoapClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : 
            base(binding, remoteAddress)
    {
    }
    
    public int Add(int intA, int intB)
    {
        return base.Channel.Add(intA, intB);
    }
    
    public System.Threading.Tasks.Task<int> AddAsync(int intA, int intB)
    {
        return base.Channel.AddAsync(intA, intB);
    }
    
    public int Subtract(int intA, int intB)
    {
        return base.Channel.Subtract(intA, intB);
    }
    
    public System.Threading.Tasks.Task<int> SubtractAsync(int intA, int intB)
    {
        return base.Channel.SubtractAsync(intA, intB);
    }
    
    public int Multiply(int intA, int intB)
    {
        return base.Channel.Multiply(intA, intB);
    }
    
    public System.Threading.Tasks.Task<int> MultiplyAsync(int intA, int intB)
    {
        return base.Channel.MultiplyAsync(intA, intB);
    }
    
    public int Divide(int intA, int intB)
    {
        return base.Channel.Divide(intA, intB);
    }
    
    public System.Threading.Tasks.Task<int> DivideAsync(int intA, int intB)
    {
        return base.Channel.DivideAsync(intA, intB);
    }
}

 

Next , is to add this class file which i call my GenericProxy class that i use to make calls to any webservice class in my codes.  It is a generic class so it is highly re-usable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;

namespace CallWebService.Core
{
    internal sealed class GenericProxy<TContract> : IDisposable where TContract : class
    {
        private readonly ChannelFactory<TContract> _channelFactory;
        private TContract _channel;

        public GenericProxy(Binding binding, EndpointAddress remoteAddress)
        {
            _channelFactory = new ChannelFactory<TContract>(binding, remoteAddress);
        }

        public void Execute(Action<TContract> action)
        {
            action.Invoke(Channel);
        }

        public TResult Execute<TResult>(Func<TContract, TResult> function)
        {
            return function.Invoke(Channel);
        }

        private TContract Channel
        {
            get
            {
                if (_channel == null)
                {
                    _channel = _channelFactory.CreateChannel();
                }

                return _channel;
            }
        }

        public void Dispose()
        {
            try
            {
                if (_channel != null)
                {
                    var currentChannel = _channel as IClientChannel;
                    if (currentChannel != null && currentChannel.State == CommunicationState.Faulted)
                    {
                        currentChannel.Abort();
                    }
                    else
                    {
                        currentChannel?.Close();
                    }
                }
            }
            finally
            {
                _channel = null;
                GC.SuppressFinalize(this);
            }
        }
    }
}

 

Now all setup is ready , we are now ready to write our codes.

Add an interface for our calculator which i call ICALCULATOR service.

namespace CallWebService.Core
{
    public interface ICalculatorService
    {
        int Sum(int a, int b);
    }
}


Next is the CalculatorService which implements the ICalculator interface above and also call the ADD method/operation in the webservice , we need to pass the interface of the webservice to the GenericProxy class with the Binding settings and the EndPoint of the service.

The EndPoint of the webservice is stored in our appsettings.json which i will call with a class i call Configurations

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Text;
using Serilog;

namespace CallWebService.Core
{
    public class CalculatorService : ICalculatorService
    {
        private static readonly EndpointAddress Endpoint = new EndpointAddress(Configurations.CalculatorServiceEndPoint);
        private static readonly BasicHttpBinding Binding = new BasicHttpBinding
        {
            MaxReceivedMessageSize = 2147483647,
            MaxBufferSize = 2147483647
        };

        public int Sum(int a, int b)
        {
            int result = 0;
            using (var proxy = new GenericProxy<CalculatorSoap>(Binding, Endpoint))
            {
                try
                {
                    result = proxy.Execute(c => c.Add(a, b));
                }
                catch (FaultException ex)
                {
                    Log.Logger.Error(ex, "error while adding numbers.");

                }
                catch (Exception ex)
                {
                    Log.Logger.Error(ex, "error while adding numbers.");
                }

                return result;
            }

        }

    }
}

Configurations Class

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IO;

namespace CallWebService.Core
{
    public static class Configurations
    {
        private static readonly IConfigurationRoot Configuration = new BootStrap().Setup();

        public static readonly string CalculatorServiceEndPoint = Configuration["WebService:CalculatorEndPoint"];

        private class BootStrap
        {
            public IConfigurationRoot Setup()
            {
                IHostingEnvironment environment = new HostingEnvironment();

                // Enable to app to read json setting files
                var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true)
                    .AddEnvironmentVariables();

                if (environment.IsDevelopment())
                {
                    // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
                    builder.AddApplicationInsightsSettings(developerMode: true);
                }

                return builder.Build();
            }
        }
    }
}

 

AppSettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "WebService": {
    "CalculatorEndPoint": "http://www.dneonline.com/calculator.asmx?wsdl"
  } 
}

At this stage we are done with all we need  to do to call our webservice in our library project above , next is to move to the ASP.NET CORE project , in the Startup.cs we need to register our ICalculatorService dependency as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using CallWebService.Core;

namespace CallWebService
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            services.AddTransient<ICalculatorService, CalculatorService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

 

For testing purpose we will be calling the webservice in our HomeController

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CallWebService.Core;
using Microsoft.AspNetCore.Mvc;

namespace CallWebService.Controllers
{
    public class HomeController : Controller
    {
        private readonly ICalculatorService _calculatorService;

        public HomeController(ICalculatorService calculatorService)
        {
            _calculatorService = calculatorService;
        }

        [HttpGet]
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Index(int firstNumber , int secondNumber)
        {
            var result = _calculatorService.Add(firstNumber, secondNumber);
            ViewBag.Result = $"{firstNumber} + {secondNumber} = {result}";
            return View();
        }
    }
}

Index.cshtml for the Index view in the HomeController

@{
    ViewData["Title"] = "Home Page";
}

<form asp-controller="Home" asp-action="Index" method="post">
    <div class="row">
        <div class="col-md-6">
            <input type="text" name="firstNumber" />
        </div>
        <br />
        <div class="col-md-6">
            <input type="text" name="secondNumber" />
        </div>
        <br />
        <input type="submit" value="Add Numbers" />
        <br/>
        @ViewBag.Result 
    </div>
</form>

sunm

ans


Finally , we have our result from the webservice.

The complete source code is available on my my github repo for download.

NOTE : You can only run it with VISUAL STUDIO 2017.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s