65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular2 & WebApi (SPA) 用于企业应用 - 第 7 部分 - 管理应用程序生命周期

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2016年12月2日

CPOL

5分钟阅读

viewsIcon

29727

在本文中,我们将学习为什么需要管理应用程序的各个阶段。

本系列的其他文章

  1. 概述
  2. 添加新权限
  3. 项目结构
  4. 多语言 (i18n)
  5. DI & IoC - 为什么以及为什么不?
  6. RESTful & WebApi
  7. 管理应用生命周期
  8. 构建和部署应用
  9. TinyERP新版本(使用Angular 2 (typescript))
  10. CQRS:避免企业应用程序中的性能问题(基础)
  11. 多个数据存储:扩展你的存储库(第1部分)
  12. 多个数据存储:扩展你的存储库(第2部分)

我们将从本文中学到什么?

在本文中,我们将

- 概述应用程序生命周期

- 为什么需要管理应用程序的生命周期

- 如何引发和处理应用程序事件,例如:应用程序错误、应用程序启动等

什么是应用程序生命周期

应用程序生命周期是应用程序从开始到结束所经历的各个阶段的集合。

例如,某些应用程序可以有这些阶段

  • 应用程序启动时
  • 应用程序错误时
  • 应用程序结束时

为什么需要管理应用程序的生命周期

这将帮助我们在事件发生时执行指定的动作。例如,在许多应用程序中,我们希望

  • 当发生未处理的异常时,向系统管理员发送电子邮件。
  • 在应用程序运行之前创建数据
  • 在退出前存储应用程序的状态。

我们的应用程序应该有多少个阶段?

实际上,我们没有应用程序应有的静态阶段列表。它取决于我们想要处理的应用程序事件。

在我的代码中,我们有以下阶段

OnApplicationStarted(应用程序启动时)这将在应用程序启动时调用
OnApplicationReady(应用程序就绪时)这将在应用程序准备好使用后立即调用。在大多数应用程序中,我们在OnApplicationStarted阶段配置IoC和DI,并在OnApplicationReady中创建默认数据。
OnApplicationRequestStarted(应用程序请求启动时)对于Web应用程序,这将在每次请求发送到服务器时调用。
OnApplicationRequestEnded(应用程序请求结束时)对于Web应用程序,这将在每次对服务器的请求结束时调用。
OnUnHandledError(未处理错误时)这将在应用程序中每个未处理的异常发生时调用
OnApplicationEnded(应用程序结束时)这将在应用程序终止时调用。通常,我们将应用程序状态存储到存储(数据库、文件等)中
  

我们可以有哪些类型的应用程序?

目前,我们有以下类型的应用程序

控制台我们用它来管理控制台应用程序的状态
WebApi我们用它来管理WebAPI应用程序的状态。这通常提供公共API
MVC我们用它来管理MVC应用程序的状态。
UnitTest(单元测试)我们用它来管理单元测试应用程序的状态。
IntegrationTest(集成测试)我们用它来管理集成测试应用程序的状态。

 

这令人困惑,您能给我一个WebAPI应用程序的具体示例吗?

我们按照以下步骤为我们的应用程序注册事件处理程序

在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();
        }
    }
}

请注意

  • 我们使用ApplicationFactory创建IApplication的新实例,并将ApplicationType.WebApi作为create函数的参数。
  • "OnApplicationStarted"(我们的应用程序生命周期事件)从"Application_Start"(WebAPI应用程序生命周期事件)调用。
  • "OnApplicationEnded"(我们的应用程序生命周期事件)从"Application_End"(WebAPI应用程序生命周期事件)调用。

在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);
            }
        }
    }
}

在此类中,我们创建WebApiApplication的新实例(实现IApplication接口)。

在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();
        }
    }
}

在此类中,我们主要将WepAPI应用程序的事件映射到我们的应用程序事件

API应用程序事件我们的应用程序事件
BeginRequestOnApplicationRequestStarted(应用程序请求启动时)
EndRequestOnApplicationRequestEnded(应用程序请求结束时)
Error(错误)OnUnHandledError(未处理错误时)
Application_Start(在Global.asax中)OnApplicationStarted(应用程序启动时)
Application_End(在Global.asax中)OnApplicationEnded(应用程序结束时)

此类也继承自BaseApplication类

在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);
        }
    }
}

应用程序的每个阶段都有其自己的接口。

要处理应用程序的指定阶段(例如,OnApplicationStarted),我们应该创建一个新类并实现相应的接口(本例中为IApplicationStartedTask)。

当该事件发生时,这些类将自动执行。

我希望在应用程序启动时写入日志文件,您能告诉我们如何写入吗?

为此,我们需要创建一个新类,该类继承自IApplicationStartedTask,名为"WriteLogOnApplicationStartedTask",如下所示

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");
        }
    }
}

当用户首次请求您的API时,您的任务将被调用

 

为什么我们需要在类的构造函数中指定"ApplicationType.WebApi"?

此参数表示我们希望该任务仅在WebApi应用程序中运行。

在您的任务的"Execute"方法中,我们将根据正在运行的应用程序(WebAPI、Console、Unittest等)检查此任务是否有效。

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

假设我们在"Console"应用程序中,那么IsValid将返回false,您的任务将不会执行。

我可以设置任务的执行顺序吗?

是的,可以。在某些情况下,我们需要我们的任务在其他任务之后执行。

"WriteLogOnApplicationStartedTask"继承自具有"Order"属性的"BaseTasks"。

默认情况下,所有任务的Order都为零。我们可以将其设置为1,2,3或更高。在执行时,这些任务将按顺序执行(1,2,3,......)。

所以我们的类将变为

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

Webapi已经有这些事件了,为什么我们需要自己的应用程序生命周期管理?

在我看来,应用程序就像一条生活在水中(.Net框架)的鱼。

 

未来,我们可以有容器,例如:将应用程序从Windows应用程序迁移到Web。

应用程序应该能够通过很少的更改就能很好地工作。

因此,为了能够管理我们应用程序的生命周期。我们只需要将我们的代码映射到新环境中的适当事件即可。

例如,关于WriteLogOnApplicationStartedTask。

在Web环境中,我们按如下方式注册
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();
    }
}

注意:在Application_Start中,我们触发OnApplicationStarted。

但在Unittest应用程序中,我们将按如下方式注册
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();
    }

}

注意:在TestInitialize阶段,我们触发OnApplicationStarted。

我们可以为控制台应用程序做类似的事情。通过这种方式,我们的代码可以在多种形式的应用程序中重复使用,而不是在许多地方重复。

结论

本文是根据我的经验创建的。如果您觉得某些情况不正确。请给我留言进行进一步讨论。

我非常感谢您的反馈,而不是仅仅说“这是错误的”并保持沉默,因为社区是为了分享和提升我们自己

 

© . All rights reserved.