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.

Friday, August 25, 2017

Visual Studio 2017 - Azure Functions template unavailable

After installing Visual Studio 2017 version 15.3 by including Azure development workload, sometimes Azure Functions template might not be available for you in the Add New Project dialog. 

Solution
In such case, go to Tools >> Extensions and Updates... , verify update for Azure Functions and Web Jobs Tools and install the update.



Sunday, February 9, 2014

Exception when SignalR hosted using OWIN by Azure worker

I came across the following error when I tried to host SignalR using OWIN by Azure worker role.

Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. 

Upon searching over the web, I came across few links (specified some in References).

Reason is specified over here.

Fix

We need to add few configuration (specified in Snippet 1) in app.config file of the project where we have specified the SignalR. mapping code.
While incorporating the fix, we need to observe it and tweak the new version accordingly, if required. Just verify the packages.config to understand which version of Microsoft.Owin and Microsoft.Owin.Security dll got installed. Most of the time, Microsoft.Owin dll version might have got changed. Ensure the new version specified in the app.config file to be matched with the one you've installed / specified in packages.config.

Snippet 1: (app.config)

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

Snippet 2: (packages.config)

<package id="Microsoft.Owin" version="2.1.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Cors" version="2.1.0" targetFramework="net45" />
  <package id="Microsoft.Owin.Diagnostics" version="2.0.2" targetFramework="net45" />
  <package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net45" />
  <package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net45" />
  <package id="Microsoft.Owin.Security" version="2.0.2" targetFramework="net45" />
  <package id="Microsoft.Owin.SelfHost" version="2.0.2" targetFramework="net45" />

References:

http://stackoverflow.com/questions/20083185/fileloadexception-when-hosting-signalr-at-azure-worker-role-with-f
https://nuget.codeplex.com/workitem/3827

Thursday, March 21, 2013

Updating 'Microsoft.Data.OData 5.2.0' to 'Microsoft.Data.OData 5.3.0' failed


Updating 'Microsoft.Data.OData 5.2.0' to 'Microsoft.Data.OData 5.3.0' failed. Unable to find a version of 'Microsoft.AspNet.WebApi.OData' that is compatible with 'Microsoft.Data.OData 5.3.0'.

WCF Data Services 5.3.0 RTW has been released few days back. Once you install it using the tools installer, it will update the "Add Service Reference" tooling integration in Visual Studio, after which it'll create the code based on the WCF Data Services 5.3.0.  In such case, for now if you try to add service reference to a WCF Data Service in an ASP.NET MVC 4 project you might end up getting the error specified at the top of this page.

As mentioned in the error, it seems that the respective "Microsoft.AspNet.WebApi.OData" dll is not available in the last ASP.NET MVC 4 update. Hope this will be made available in the next related update.

Solution: Uninstall the respective program installed using the WCF Data Services 5.3.0 RTW tools installer. If you try the service reference now it'll get you with Microsoft.Data.OData 5.2.0 in the client. This way we can go with WCF Data Service created with 5.3.0 version and the respective client libraries (proxy....) with 5.2.0 (in ASP.NET MVC 4 client projects). Just tried locally, but didn't verified entire features.
As an alternate try with Nuget packages.
Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.