Other Articles in the series
- Overview
- Add new Permission
- Project structure
- Multi-Languages (i18n)
- DI & IoC - Why and Why not?
- RESTful & WebApi
- Manage Application Lifecycle
- Build & Deploy Application
- New version of TinyERP with Angular 2 (typescript)
- CQRS: Avoiding performance issues in enterprise app (basic)
- Multiple data-stores: Scale your repository (Part 1)
- 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:
OnApplicationStarted | This will be called when the app was started |
OnApplicationReady | this 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. |
OnApplicationRequestStarted | For web application, this was called on every request posted to server. |
OnApplicationRequestEnded | For web application, this was called on every request to server was ended. |
OnUnHandledError | This was called on every exception in the app that was not handled |
OnApplicationEnded | was 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:
Console | We use this to manage the state of console application |
WebApi | We use this to manage the state of WebAPI application. This usually provide the public API |
MVC | We use this to manage the state of MVC application. |
UnitTest | We use this to manage the state of Unittest application. |
IntegrationTest | We 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 Event | Our Application Event |
---|
BeginRequest | OnApplicationRequestStarted |
EndRequest | OnApplicationRequestEnded |
Error | OnUnHandledError |
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:
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).
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.
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.