ASP.NET 4.0 – Pre Application Start Method

在ASP.NET 4.0中,有一個很有趣的特色:Pre Application Start Method,原本在ASP.NET中,當應用程式起始時首先被呼叫的是Global.asax中的Application_Start函式,新增的Pre Application Start Method機制

則稍微改變了這個流程,當某個Assembly標示了PreApplicationStar Method Attribute後,ASP.NET會在應用程式起始時呼叫指定的函式,這個動作發生在Global.asax中的Application_Start之前

ASP.NET 4.0 – Pre Application Start Method

 

 

/黃忠成

 

  在ASP.NET 4.0中,有一個很有趣的特色:Pre Application Start Method,原本在ASP.NET中,當應用程式起始時首先被呼叫的是Global.asax中的Application_Start函式,新增的Pre Application Start Method機制

則稍微改變了這個流程,當某個Assembly標示了PreApplicationStar Method Attribute後,ASP.NET會在應用程式起始時呼叫指定的函式,這個動作發生在Global.asax中的Application_Start之前,而且是自動掃描

BIN目錄中的的Assemblys來進行的。

  這意味著,我們可以撰寫一個Class Library,標記某個函式為Pre Application Start Method,只要將其放入Web應用程式的Bin目錄下,該Method會被自動執行,這在DI(Dependency Injection)及IoC情況下特別有用,

因為應用程式不需要主動的呼叫初始化DI Container的函式,而且可以形成一種即插即用的情境、

 

實作

 

  運用這個機制的步驟很簡單,首先建立一個Web 應用程式,接著以Service Container概念建立一個Class Library。

 

ServiceLib.cs(ServiceLibrary Project)

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

 

namespaceServiceLibrary

{

    public interface IService

    {

    }

 

    public interface IExecutableService

    {

        object Execute(object param);

    }

 

    public abstract class ServiceDescriptor

    {

        private IService _serviceObject = null;

 

        public string ServiceName { get; set; }

        public IService ServiceObject

        {

            get

            {

                if (_serviceObject == null)

                    _serviceObject = CreateServiceObject();

                return _serviceObject;

            }

        }

 

        public abstract IService CreateServiceObject();

    }

 

    public class TypedServiceDescriptor:ServiceDescriptor

    {

        public Type ServiceType { get; set; }

 

        public override IService CreateServiceObject()

        {

            return Activator.CreateInstance(ServiceType) as IService;

        }

    }

 

    public static class ServiceContainer

    {

        private static object _lock = new object();

        private static Dictionary<string, ServiceDescriptor> _services = null;

 

        private static Dictionary<string, ServiceDescriptor> Services

        {

            get

            {

                if (_services == null)

                    _services = new Dictionary<string, ServiceDescriptor>();

                return _services;

            }

        }

 

        public static void RegisterServcie(ServiceDescriptor descriptor)

        {

            lock (_lock)

            {

                if (Services.ContainsKey(descriptor.ServiceName))

                    throw new Exception(string.Format("service {0} is registered.", descriptor.ServiceName));

                Services.Add(descriptor.ServiceName, descriptor);

            }

        }

 

        public static void UnRegisterService(ServiceDescriptor descriptor)

        {

            if (Services.ContainsKey(descriptor.ServiceName))

                Services.Remove(descriptor.ServiceName);

        }

 

        public static IService GetService(string serviceName)

        {

            if (Services.ContainsKey(serviceName))

                return Services[serviceName].CreateServiceObject();

            throw new Exception(string.Format("service {0} is not exists.", serviceName));

        }

 

        public static T GetService<T>(string serviceName)

        {

            return (T)GetService(serviceName);

        }

    }

 

}

 

讓Web應用程式參考此Class Library,並於Web From中放入一個Button並撰寫以下程式碼。

 

WebForm1.aspx.cs

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Web;

usingSystem.Web.UI;

usingSystem.Web.UI.WebControls;

usingServiceLibrary;

 

namespaceWebApplication3

{

    public partial class WebForm1 : System.Web.UI.Page

    {

        protected void Page_Load(object sender, EventArgs e)

        {

 

        }

 

        protected void Button1_Click(object sender, EventArgs e)

        {

            IExecutableService service = ServiceContainer.GetService<IExecutableService>("Sum");

            Button1.Text = service.Execute(new object[] { 15, 30 }).ToString();

        }

    }

}

在Service Container概念中,此時如果要將Service加入Container內,得尋求兩個途徑,一個是透過組態檔讀入Plug-In Service Assembly,另一個則是透過特定的函式,不管是哪種,

我們都得改變Web Application中的程式碼,例:

 

ServiceContainer.Initialize(….)

但透過Pre Application Start Method機制,我們有另一個選擇。

請建立另一個Class Library,用作Service提供者。

 

SumService.cs(SumService Project)

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingServiceLibrary;

 

namespaceSumService

{

    public class SumService:IService, IExecutableService

    {

        public object Execute(object param)

        {

            object[] p = (object[])param;

            return (int)p[0] + (int)p[1];

        }

    }

 

    public static class PreApplicationStartCode

    {

        private static bool _startWasCalled;

 

        public static void Start()

        {

            if (!_startWasCalled)

            {

                _startWasCalled = true;

                ServiceContainer.RegisterServcie(new TypedServiceDescriptor() { ServiceName = "Sum", ServiceType = typeof(SumService) });

            }

        }

    }

}

重點來了,在SumService這個Class Library中的AssemblyInfo.cs中加入PreApplicationStartMethod Attribute。

 

AssemblyInfo.cs(SumService Project)

usingSystem.Reflection;

usingSystem.Runtime.CompilerServices;

usingSystem.Runtime.InteropServices;

usingSystem.Web;

 

// General Information about an assembly is controlled through the following

// set of attributes. Change these attribute values to modify the information

// associated with an assembly.

[assembly: AssemblyTitle("SumService")]

[assembly: AssemblyDescription("")]

[assembly: AssemblyConfiguration("")]

[assembly: AssemblyCompany("Microsoft")]

[assembly: AssemblyProduct("SumService")]

[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]

[assembly: AssemblyTrademark("")]

[assembly: AssemblyCulture("")]

 

// Setting ComVisible to false makes the types in this assembly not visible

// to COM components.  If you need to access a type in this assembly from

// COM, set the ComVisible attribute to true on that type.

[assembly: ComVisible(false)]

 

// The following GUID is for the ID of the typelib if this project is exposed to COM

[assembly: Guid("67c271fc-0c4e-4be3-ac72-45b09beeeac5")]

 

// Version information for an assembly consists of the following four values:

//

//      Major Version

//      Minor Version

//      Build Number

//      Revision

//

// You can specify all the values or you can default the Build and Revision Numbers

// by using the '*' as shown below:

// [assembly: AssemblyVersion("1.0.*")]

[assembly: AssemblyVersion("1.0.0.0")]

[assembly: AssemblyFileVersion("1.0.0.0")]

[assembly: PreApplicationStartMethod(typeof(SumService.PreApplicationStartCode), "Start")]

接下來,只要將SumService.dll擺到Web Application的BIN目錄下,會發現應用程式可以正常執行,因為當Web Application啟動時,會掃描BIN目錄下的所有Assembly,

當該Assembly標示了PreApplicationStartMethod時,ASP.NET Runtime便會執行該Method,接著SumService就被注入到Service Container中了。

 

 

範例下載:

http://www.code6421.com/FileHub/PreStartDemo.zip