Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Angular2 & WebApi (SPA) for Enterprise App - Part 7 - Manage Application Lifecycle

5.00/5 (9 votes)
29 May 2017CPOL5 min read 29K  
In this article, We will learn why do we need to manage the stages of our application.

Other Articles in the series

  1. Overview
  2. Add new Permission
  3. Project structure
  4. Multi-Languages (i18n)
  5. DI & IoC - Why and Why not?
  6. RESTful & WebApi
  7. Manage Application Lifecycle
  8. Build & Deploy Application
  9. New version of TinyERP with Angular 2 (typescript)
  10. CQRS: Avoiding performance issues in enterprise app (basic)
  11. Multiple data-stores: Scale your repository (Part 1)
  12. Multiple data-stores: Scale your repository (Part 2)

What will we learn from this article?

In this article, we will:

- Overview about what is application lifecycle

- Why do we need to manage lifecyle of application

- How do we raise and handle event of application, such as: on application error, on application stared, ...

What is application lifecycle

Application lifecycle is the collective of stages that the application goes through from beginning to the end of the application.

For example, some application we can have those stages:

  • On application started
  • On application error
  • On application end

Why do we need to manage lifecyle of application

This will help us can perform specified action when  an event occured. For example, in many application we want:

  • Send email to system administrator when unhandle exception occure.
  • Create data before the app runs
  • Store the state of application before exiting.

How many stages should we have for our application?

Actually, we did not have the static list of stages of the application should have. it was belong to which event of the app we want to handle.

In my code, we have the following stages:

OnApplicationStartedThis will be called when the app was started
OnApplicationReadythis was called right after the app is ready for use. In most application, we config IoC & DI, .... in OnApplicationStarted stage and create default data in OnApplicationReady.
OnApplicationRequestStartedFor web application, this was called on every request posted to server.
OnApplicationRequestEndedFor web application, this was called on every request to server was ended.
OnUnHandledErrorThis was called on every exception in the app that was not handled
OnApplicationEndedwas called when the app was terminated. Usually, we store the state of the application to storage (db, file, ...)
  

Which type of application we can have?

At this time, we have the flowing type of application:

ConsoleWe use this to manage the state of console application
WebApiWe use this to manage the state of WebAPI application. This usually provide the public API
MVCWe use this to manage the state of MVC application.
UnitTestWe use this to manage the state of Unittest application.
IntegrationTestWe use this to manage the state of integration-test application.

 

It is confusing, can you give me a concrete sample for WebAPI application?

we follow the following steps to register the event handler for our application:

In Global.asax

namespace App.Api
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        private App.Common.IApplication application;
        public WebApiApplication()
        {
            this.application = App.Common.ApplicationFactory.Create<System.Web.HttpApplication>(App.Common.ApplicationType.WebApi, this);
        }

        protected void Application_Start()
        {
            this.application.OnApplicationStarted();
        }

        protected void Application_End()
        {
            this.application.OnApplicationEnded();
        }
    }
}

Be aware that:

  • We create new instance of IApplication using ApplicationFactory and pass ApplicationType.WebApi as parameter of create function.
  • "OnApplicationStarted" (our app lifecycle event) was called from "Application_Start" (WebAPI app lifecycle event).
  • "OnApplicationEnded" (our app lifecycle event) was called from "Application_End" (WebAPI app lifecycle event).

In ApplicationFactory.cs

namespace App.Common
{
    using App.Common.Validation;

    public class ApplicationFactory
    {
        public static IApplication Create<TContext>(ApplicationType type, TContext application)
        {
            switch (type)
            {
                case ApplicationType.Console:
                    return new ConsoleApplication<TContext>(application);
                case ApplicationType.MVC:
                    return new MVCApplication<TContext>(application);
                case ApplicationType.WebApi:
                    return new WebApiApplication(application as System.Web.HttpApplication);
                case ApplicationType.UnitTest:
                    return new UnitTestApplication<TContext>(application);
                default:
                    throw new ValidationException("Common.ApplicationDoesNotSupported", type);
            }
        }
    }
}

In this class, we create new instance of WebApiApplication (implement the IApplication interface).

In WebApiApplication.cs

namespace App.Common
{
    using App.Common.Helpers;
    using App.Common.Tasks;
    using System.Web.Routing;

    public class WebApiApplication : BaseApplication<System.Web.HttpApplication>
    {
        public WebApiApplication(System.Web.HttpApplication context)
            : base(context, ApplicationType.WebApi)
        {
            this.Context.BeginRequest += this.OnBeginRequest;
            this.Context.EndRequest += this.OnEndRequest;
            this.Context.Error += this.OnError;
        }

        public override void OnApplicationStarted()
        {
            base.OnApplicationStarted();
            this.OnRouteConfigured();
        }

        private void OnRouteConfigured() {
            TaskArgument<RouteCollection> taskArg = new TaskArgument<RouteCollection>(RouteTable.Routes, this.Type);
            AssemblyHelper.ExecuteTasks<IRouteConfiguredTask, TaskArgument<RouteCollection>>(taskArg);
        }

        private void OnBeginRequest(object sender, System.EventArgs e)
        {
            this.OnApplicationRequestStarted();
        }

        private void OnEndRequest(object sender, System.EventArgs e)
        {
            this.OnApplicationRequestEnded();
        }

        private void OnError(object sender, System.EventArgs e)
        {
            this.OnUnHandledError();
        }
    }
}

In this class, we major map event of WepAPI application to our application event:

API Application EventOur Application Event
BeginRequestOnApplicationRequestStarted
EndRequestOnApplicationRequestEnded
ErrorOnUnHandledError
Application_Start (in Global.asax)OnApplicationStarted
Application_End (in Global.asax)OnApplicationEnded

This class also inherit from BaseApplication class

In BaseApplication.cs

namespace App.Common
{
    using App.Common.Helpers;
    using App.Common.Tasks;

    public class BaseApplication<TContext> : IApplication
    {
        public TContext Context { get; private set; }
        public ApplicationType Type { get; private set; }
        public BaseApplication(TContext context, ApplicationType type)
        {
            this.Context = context;
            this.Type = type;
        }

        public virtual void OnApplicationStarted()
        {
            TaskArgument<TContext> taskArg = new TaskArgument<TContext>(this.Context, this.Type);
            AssemblyHelper.ExecuteTasks<IApplicationStartedTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg);
            AssemblyHelper.ExecuteTasks<IApplicationReadyTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg, true);
        }

        public virtual void OnApplicationEnded()
        {
            TaskArgument<TContext> taskArg = new TaskArgument<TContext>(this.Context, this.Type);
            AssemblyHelper.ExecuteTasks<IApplicationEndedTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg);
        }

        public virtual void OnApplicationRequestStarted()
        {
            TaskArgument<TContext> taskArg = new TaskArgument<TContext>(this.Context, this.Type);
            AssemblyHelper.ExecuteTasks<IApplicationRequestStartedTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg);
        }

        public virtual void OnApplicationRequestEnded()
        {
            TaskArgument<TContext> taskArg = new TaskArgument<TContext>(this.Context, this.Type);
            AssemblyHelper.ExecuteTasks<IApplicationRequestEndedTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg);
        }

        public virtual void OnUnHandledError()
        {
            TaskArgument<TContext> taskArg = new TaskArgument<TContext>(this.Context, this.Type);
            AssemblyHelper.ExecuteTasks<IUnHandledErrorTask<TaskArgument<TContext>>, TaskArgument<TContext>>(taskArg);
        }
    }
}

Each stage of application has it own inteface.

To handle specified stage of applicaiton (for example, OnApplicationStarted), we should create new class and implement appropriated interface (IApplicationStartedTask in this case).

these class will be executed automaticly when that event occurred.

I want to write into log file when the applicaiton was started, Can you show us how to write?

To do this, we need to create new class that inherit from IApplicationStartedTask named "WriteLogOnApplicationStartedTask" as below:

namespace App.Api.Features.Share.Tasks.Common
{
    using System.Web;
    using App.Common.Tasks;
    using System;

    public class WriteLogOnApplicationStartedTask : BaseTask<TaskArgument<System.Web.HttpApplication>>, IApplicationStartedTask<TaskArgument<System.Web.HttpApplication>>
    {
        public WriteLogOnApplicationStartedTask() : base(App.Common.ApplicationType.WebApi){}

        public override void Execute(TaskArgument<HttpApplication> context)
        {
            if (!this.IsValid(arg.Type)) { return; }
            Console.WriteLine("This for test only: Application was started");
        }
    }
}

When user make the first request to your API, your tasks will be called:

Image 1

 

Why we need to specify "ApplicationType.WebApi" in constructor of my class?

this parameter indicate that the task we expect to run in WebApi application only.

In the "Execute" method of you tasks, we will check if this task is valid to run base on the application is running (WebAPI, Console, Unittest, ...)

if (!this.IsValid(arg.Type)) { return; }

Let assume, we are in "Console" application, So IsValid will return false, and your task will be not executed.

Can I set the order that my task will be executed?

Yes, we can. There are some cases, we need our task to be executed after others.

"WriteLogOnApplicationStartedTask" inherits from "BaseTasks" that have "Order" property.

By default, Order of all tasks are zero. We can set it to 1,2,3 or higher. At execution time, those tasks will be executed in order (1,2,3,.....).

So our class will be changed to:

public WriteLogOnApplicationStartedTask() : base(App.Common.ApplicationType.WebApi){
    this.Order = 10;
}

Webapi already have these event, Why we need our own application life-cycle maangement?

From my view, the application as a fish that was existing in water (.Net framework).

Image 2

 

And in the future, we can the container, such as: migrate the application from window application to Web.

The app should be abpe to work well with a little change.

Image 3

So, be able to manage the life-cycle of our application. We only need to map our code to appropriated event on new environtment.

For example about WriteLogOnApplicationStartedTask.

In Web environtment, we register as below:
public class WebApiApplication : System.Web.HttpApplication
{
    private App.Common.IApplication application;
    public WebApiApplication()
    {
        this.application = App.Common.ApplicationFactory.Create<System.Web.HttpApplication>(App.Common.ApplicationType.WebApi, this);
    }
    protected void Application_Start()
    {
        this.application.OnApplicationStarted();
    }
}

Note: in Application_Start, we trigger OnApplicationStarted.

But in Unittest application, we will register as below:
public abstract class BaseUnitTest
{
    public App.Common.IApplication Application { get; protected set; }
    public BaseUnitTest()
    {
        this.Application = App.Common.ApplicationFactory.Create<System.Web.HttpApplication>(App.Common.ApplicationType.UnitTest, null);
    }

    [TestInitialize()]
    public void Init()
    {
        this.Application.OnApplicationStarted();
    }

}

Note: in TestInitialize phase, we trigger OnApplicationStarted.

We can do similar for Console application. By this way, our code can be re-used across many form of application instead of duplication in many places.

Conclusion

The article was created from my experienced. If you feel some cases are incorrect. Please write me an message for futher discussion.

I really appriciate your feedback rather than just said "it is wrong" and keep silent as community for sharing and improving ourselves.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)