Monday, October 30, 2017

Time triggered / Scheduler service with Azure Service Fabric

Introduction

Azure Service Fabric Services can be configured to exhibit trigger based behavior similar to WebJobs and Azure Functions.

Time triggered / Scheduler Service in Azure Service Fabric

To create a time triggered (scheduler) Service in Azure Service Fabric, I can think of following two quick options among the possible ways.
  1. Create a time triggered WebJob and add/deploy it as a guest executable in Azure Service Fabric.
  2. Create Azure Service Fabric Stateless Service and implement the listener to handle jobs.
Here I'm taking the second approach which involves the respective listener to be created. Rather than writing new Scheduling framework, I'm using the Quartz.Net framework. I'm using the CRON expression behavior to have the behavior inline with the time trigger behavior of WebJobs and Azure Functions.

I'll be explaining two approaches below.
  1. Simple Time trigger Service
  2. Time trigger Service using Dependency Injection

Simple Time trigger Service

  • Uses Quartz.Net framework.
  • Without Dependency Injection.
  • Listerner gets all the IJob types from the current assembly with JobInfoAttribute decorated.
  • JobInfoAttribute holds the name and CRON expression of the job.
  • Code samples can be found in my github repo here.

Snippet 1 (SimpleTimeTriggerListener)

using Microsoft.ServiceFabric.Services.Communication.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Reflection;
using Quartz;
using TimeTriggerService;
using Quartz.Impl;

namespace SimpleTimeTriggerService
{
    public class SimpleTimeTriggerListener : ICommunicationListener
    {
        private IScheduler scheduler;

        public void Abort()
        {
            scheduler.Shutdown(false);
        }

        public Task CloseAsync(CancellationToken cancellationToken)
        {
            scheduler.Shutdown(true);
            return Task.FromResult(true);
        }

        public Task<string> OpenAsync(CancellationToken cancellationToken)
        {
            var tasks =
                Assembly.GetExecutingAssembly()
                    .GetTypes()
                    .Where(
                        t =>
                            typeof(IJob).IsAssignableFrom(t) &&
                            Attribute.IsDefined(t, typeof(JobInfoAttribute)));

            foreach (var task in tasks)
            {
                var schedulerFactory = new StdSchedulerFactory();
                scheduler = schedulerFactory.GetScheduler();
                var jobInfo = Attribute.GetCustomAttribute(task, typeof(JobInfoAttribute)) as JobInfoAttribute;
                var jobName = jobInfo == null ? task.Name : jobInfo.Name;
                var job = new JobDetailImpl(jobName, null, task);

                var trigger =
                    TriggerBuilder.Create()
                        .WithIdentity($"{jobName}Trigger", null)
                        .WithCronSchedule(jobInfo.CronExpression)
                        .ForJob(job)
                        .Build();
                scheduler.ScheduleJob(job, trigger);                
            }

            scheduler.Start();

            return Task.FromResult("Sample Simple Job scheduler");
        }
    }
}

Time trigger Service using Dependency Injection

  • Listerner gets all the IJob types from the current assembly with JobInfoAttribute decorated.
  • JobInfoAttribute holds the name and CRON expression of the job.
  • Code samples can be found in my github repo here.



Snippet 2 (TimeTriggerListener with Autofac)

using Microsoft.ServiceFabric.Services.Communication.Runtime;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;
using Autofac;
using Quartz;
using Autofac.Extras.Quartz;
using System.Reflection;

namespace TimeTriggerService
{
    internal class TimeTriggerListener : ICommunicationListener
    {
        private IScheduler scheduler;

        public void Abort()
        {
            scheduler.Shutdown(false);
        }

        public Task CloseAsync(CancellationToken cancellationToken)
        {
            scheduler.Shutdown(true);
            return Task.FromResult(true);
        }

        public Task<string> OpenAsync(CancellationToken cancellationToken)
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterModule(new QuartzAutofacFactoryModule());
            builder.RegisterModule(new QuartzAutofacJobsModule(Assembly.GetExecutingAssembly()));
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(x => typeof(IJob).IsAssignableFrom(x)).As<IJob>();
            builder.RegisterType<Logger>().As<ILogger>();
            builder.RegisterType<JobScheduler>().AsSelf();
            IContainer container = builder.Build();
            ConfigureScheduler(container);

            return Task.FromResult("Sample Job scheduler");
        }
        
        private void ConfigureScheduler(IContainer container)
        {
            IEnumerable<IJob> jobList = container.Resolve<IEnumerable<IJob>>();
            var jobScheduler = container.Resolve<JobScheduler>();
            this.scheduler = jobScheduler.Start();
        }
    }
}



Snippet 3 (JobScheduler)

using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;

namespace TimeTriggerService
{
    public class JobScheduler
    {
        private IScheduler scheduler;
        private IEnumerable<IJob> jobs;
        public JobScheduler(IScheduler schedulerParam, IEnumerable<IJob> jobsParam)
        {
            this.scheduler = schedulerParam;
            this.jobs = jobsParam;
        }

        public IScheduler Start()
        {
            foreach (var job in jobs)
            {
                var task = job.GetType();
                var jobInfo = Attribute.GetCustomAttribute(task, typeof(JobInfoAttribute)) as JobInfoAttribute;
                var jobName = jobInfo == null ? task.Name : jobInfo.Name;
                var jobDetail = new JobDetailImpl(jobName, null, task);
                var trigger =
                    TriggerBuilder.Create()
                        .WithIdentity($"{jobName}Trigger", null)
                        .WithCronSchedule(jobInfo.CronExpression)
                        .ForJob(jobDetail)
                        .Build();
                scheduler.ScheduleJob(jobDetail, trigger);
            }
            scheduler.Start();

            return scheduler;
        }
    }
}


Full source code has been provided here.

Applying strict boolean behavior in ASP.NET WEB API JSON request model

Introduction

In ASP.NET WEB API / MVC in the request input model if you have some property that accepts bool type with content type application/json, it might even accept numbers which will be converted to bool type. If you send numbers as input for bool, it'll be converted to false for 0 and true for everything else (with an exception observed - 08 and 09 as null). 

Problem statement

To restrict from numbers being accepted as input for bool type property in JSON content type.

Solution

  1. Create a converter that inherits from Newtonsoft.Json.JsonConverter.
  2. Override ReadJson method, check the ValueType for bool type and throw JSON serializer exception if not.

Snippet (Converter)

public class BooleanConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.ValueType != typeof(bool))
        {
            throw new JsonSerializationException("Invalid data type");
        }
        return reader.Value;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(bool);
    }
}

Saturday, October 28, 2017

Calculate execution time for your ASP.NET WEB API controller actions using Autofac action filter

Requirement

  • Calculate execution time of ASP.NET WEB API actions
  • Create a common code that can calculate and log the details
  • Project uses Autofac for Dependency Injection.
If you've integrated Azure Application Insights to your WEB API, automatically it'll log/capture the execution time of each request. If you need it as additional logging or in place where Application Insights not associated, this will be helpful. Let us get into the code directly.

Approach 

  1. We will be using ActionFilter to write the logic for calculating the execution time.
  2. In case of WEB API that uses Autofac as dependency resolver, we can utilize the IAutofacActionFilter to achieve the same.
  3. Register the respective action filter globally to execute for all controllers and actions, so that you don't need to worry when you add new controllers/actions.
  4. In the below example I provided the sample snippet related to Autofac based solution. Ignore the Custom logger in the below example as it is just for portraying the sample.

Snippet1: (Action Filter that calculates the execution time and logs)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Autofac.Integration.WebApi;

namespace TestWebAPI1.Utils
{
    public class ExecutionTimeActionFilterAttribute : IAutofacActionFilter
    {
        private const string StopwatchInstanceKey = "StopwatchInstance";

        private readonly ICustomLogger1 logger;

        public ExecutionTimeActionFilterAttribute(ICustomLogger1 loggerParam)
        {
            this.logger = loggerParam;
        }


        public void OnActionExecuting(HttpActionContext actionContext)
        {
            Stopwatch stopwatchInstance = new Stopwatch();
            actionContext.Request.Properties[StopwatchInstanceKey] = stopwatchInstance;
            stopwatchInstance.Start();
        }

        public void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (!actionExecutedContext.Request.Properties.ContainsKey(StopwatchInstanceKey))
            {
                return;
            }

            Stopwatch stopwatchInstance = actionExecutedContext.Request.Properties[StopwatchInstanceKey] as Stopwatch;
            if (stopwatchInstance == null)
            {
                return;
            }

            stopwatchInstance.Stop();
            string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;

            this.logger.WriteLine($"Execution time of action : {actionName} in controller : {actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName} is {stopwatchInstance.ElapsedMilliseconds} milliseconds");
        }
    }
}

Snippet 2: (Autofac registration in WebApiConfig)

namespace TestWebAPI1
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var builder = new ContainerBuilder();
            
            builder.RegisterType<CustomLogger1>().As<ICustomLogger1>().InstancePerDependency();
            
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            
            builder.RegisterWebApiFilterProvider(config);
            builder.Register(c => new ExecutionTimeActionFilterAttribute(c.Resolve<ICustomLogger1>()))
                .AsWebApiActionFilterFor<ApiController>()
                .InstancePerRequest();

            var container = builder.Build();
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        }
    }
}

References

The concept has been taken based on the suggestions by some blogs and stackoverflow forums (unable to recall exact article).

Web Deployment package Zip not getting created even after specifying required MSBuild args in Visual Studio Build task

Introduction

Visual Studio Team Services (VSTS) provide the facilitation to incorporate Continuous Integration and Continuous deployment. For Azure WebApp/WebJobs Documentation and Blogs are available (like this, this ) to guide you how to achieve those. In this post we're going to have a look at scenario where the Web deployment package itself might not get created and solution for that.

Scenario


  • Visual Studio build task has been added and configured appropriately.
  • Respective MSBuild arguments "/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation=$(Build.BinariesDirectory)" has been provided.
But even after that Web deployment package zip file not getting created.

Possible Cause and Solution

Ensure you have the Microsoft.Web.WebJobs.Publish NuGet package added to the respective project. If the package is not associated, in your CI execution Buildstep won't throw any exception, rather it simply won't create the web deployment zip package.
Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.