在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