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.

SweetAlert Notification in ASP.NET MVC & ASP.NET CORE

I got a requirement in an application to display notification messages as beautiful pop-ups instead of plain text on the web page.

Note , this is not a toastr kind of notification , it is the normal javascript alert notification.

A quick search and i got hold of this beautiful alert project by Tristan Edwards called SweetAlert

I thought well about this and also how i need to implement it to make it re-usable and available in all my Controller without doing too much frontend implementation of it.

Hence , i had to come up with a BaseController which all other controllers will simply inherit  from.

BASECONTROLLER DESIGN

    public abstract class BaseController : Controller
    {
       
        public void Alert(string message , NotificationType notificationType)
        {
           var msg = "swal('" + notificationType.ToString().ToUpper() + "', '"+ message +"','"+ notificationType+"')" + "";
           TempData["notification"] = msg;
        }
    }

Then a sample controller that inherit from the BASECONTROLLER above.

public class TestController : BaseController
 {
        [HttpGet]
        public IActionResult Index()
        {
            return View();
        }
    }
}

Since this TESTCONTROLLER inherits from the BASECONTROLLER , i therefore have access to the Alert method in the BASECONTROLLER.

The alert has icons that depict the kind of alert i want to use i.e error notification , success notification , warning notification and info notification

Hence the need for an enum , so i create the enum class below , which already appeared in the BASECONTROLLER above.

   public class Enum
    {
        public enum NotificationType
        {
            error,
            success,
            warning,
            info
        }

    }

With completely re-usability in mind , let us create a partial view that will display the alert notification passed into TempData[“notification”] = msg; in the BASECONTROLLER  above.

So let us create a partial view called _NotificationPanel.cshtml file which we will call in the _Layout.cshtml file in the Views/Shared folder

@if (TempData["notification"] != null)
{
    @Html.Raw(TempData["notification"])
}

Then in the _Layout.cshtml we make the _NotifcationPanel.cshtml available like this

@Html.Partial(“_NotificationPanel”)

@RenderBody()

Also , we need to add the css and javascript we got from SweetAlert to the  HEAD section of the _Layout.cshtml file

<link href=”/sweetAlert/sweetalert.css” rel=”stylesheet” />
/sweetAlert/sweetalert.min.js

Finally note that the view of our TESTCONTROLLER above need to use the _Layout as it’s Layout , so our TESTCONTROLLER view page needs to look like this

@{
    ViewBag.Title = "title";
    Layout = "_Layout";
}

Now we are set for use. So let us create sample for all the SWEETALERT notification types.

For Success Messages :

Our TESTCONTROLLER will be :

For Success Messages :

   public class TestController : BaseController
    {
        [HttpGet]
        public IActionResult Index()
        {          
            Alert("This is success message",NotificationType.success);
            return View();
        }
    }

With the help of our enum class we can choose any kind of message we want.

Source code available on my github repo.

Thanks…please do leave your comments below.