Permalink
Browse files

Added ApplicationShutdownMethod support + cleanup

  • Loading branch information...
davidebbo committed Mar 1, 2011
1 parent c1b8c86 commit 586b23d6fad2a08c3fe668e6f98ea880fea42339
@@ -1,28 +1,53 @@
using System.Web.Mvc;
using System.Web.Routing;
+using System;
[assembly: WebActivator.PreApplicationStartMethod(typeof(TestLibrary.MyStartupCode), "Start")]
[assembly: WebActivator.PreApplicationStartMethod(typeof(TestLibrary.MyStartupCode), "Start2")]
[assembly: WebActivator.PostApplicationStartMethod(typeof(TestLibrary.MyStartupCode), "CallMeAfterAppStart")]
+[assembly: WebActivator.ApplicationShutdownMethod(typeof(TestLibrary.MyStartupCode), "CallMeWhenAppEnds")]
namespace TestLibrary {
public static class MyStartupCode {
public static bool StartCalled { get; set; }
public static bool Start2Called { get; set; }
public static bool CallMeAfterAppStartCalled { get; set; }
+ public static bool CallMeWhenAppEndsCalled { get; set; }
internal static void Start() {
+ if (StartCalled) {
+ throw new Exception("Unexpected second call to Start");
+ }
+
StartCalled = true;
}
public static void Start2() {
+ if (Start2Called) {
+ throw new Exception("Unexpected second call to Start2");
+ }
+
Start2Called = true;
}
public static void CallMeAfterAppStart() {
// This gets called after global.asax's Application_Start
+ if (CallMeAfterAppStartCalled) {
+ throw new Exception("Unexpected second call to CallMeAfterAppStart");
+ }
+
CallMeAfterAppStartCalled = true;
}
+
+ public static void CallMeWhenAppEnds() {
+ // This gets called when the app shuts down
+
+ if (CallMeWhenAppEndsCalled) {
+ throw new Exception("Unexpected second call to CallMeWhenAppEnds");
+ }
+
+ CallMeWhenAppEndsCalled = true;
+ }
}
}
@@ -4,10 +4,24 @@
using System.Web;
[assembly: WebActivator.PostApplicationStartMethod(typeof(AppCodeStartupCode), "Start")]
+[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(AppCodeStartupCode), "Shutdown")]
public class AppCodeStartupCode {
- public static bool Called { get; set; }
+ public static bool StartCalled { get; set; }
public static void Start() {
- Called = true;
+ if (StartCalled) {
+ throw new Exception("Unexpected second call to Start");
+ }
+
+ StartCalled = true;
+ }
+
+ public static bool ShutdownCalled { get; set; }
+ public static void Shutdown() {
+ if (ShutdownCalled) {
+ throw new Exception("Unexpected second call to Shutdown");
+ }
+
+ ShutdownCalled = true;
}
}
@@ -14,6 +14,7 @@
<AssemblyName>TestWebApp</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
+ <UseIISExpress>false</UseIISExpress>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -8,7 +8,7 @@
<%
if (!TestLibrary.MyStartupCode.StartCalled || !TestLibrary.MyStartupCode.Start2Called ||
!TestLibrary.MyStartupCode.CallMeAfterAppStartCalled || !TestWebApp.TestStartupCode.MyStartupCode.StartCalled ||
- !AppCodeStartupCode.Called) {
+ !AppCodeStartupCode.StartCalled || TestLibrary.MyStartupCode.CallMeWhenAppEndsCalled) {
throw new Exception("Startup methods were not correctly called");
}
%>
@@ -1,19 +1,19 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
-using System;
namespace WebActivator {
public class ActivationManager {
- private static bool hasInited;
+ private static bool _hasInited;
private static List<Assembly> _assemblies;
public static void Run() {
- if (!hasInited) {
+ if (!_hasInited) {
RunPreStartMethods();
// Register our module to handle any Post Start methods. But outside of ASP.NET, just run them now
@@ -24,7 +24,7 @@ public class ActivationManager {
RunPostStartMethods();
}
- hasInited = true;
+ _hasInited = true;
}
}
@@ -47,73 +47,70 @@ public class ActivationManager {
}
}
- public static void RunPreStartMethods() {
- // Go through all the relevant assemblies and run the PreStart logic
- foreach (var assembly in Assemblies) {
- foreach (PreApplicationStartMethodAttribute preStartAttrib in assembly.GetPreAppStartAttributes()) {
- // Invoke the method that the attribute points to
- preStartAttrib.InvokeMethod();
+ private static IEnumerable<string> GetAssemblyFiles() {
+ // When running under ASP.NET, find assemblies in the bin folder.
+ // Outside of ASP.NET, use whatever folder WebActivator itself is in
+ string directory = HostingEnvironment.IsHosted
+ ? HttpRuntime.BinDirectory
+ : Path.GetDirectoryName(typeof(ActivationManager).Assembly.Location);
+ return Directory.GetFiles(directory, "*.dll");
+ }
+
+ // Return all the App_Code assemblies
+ private static IEnumerable<Assembly> AppCodeAssemblies {
+ get {
+ // Return an empty list if we;re not hosted or there aren't any
+ if (!HostingEnvironment.IsHosted || !_hasInited || BuildManager.CodeAssemblies == null) {
+ return Enumerable.Empty<Assembly>();
}
+
+ return BuildManager.CodeAssemblies.OfType<Assembly>();
}
}
+ public static void RunPreStartMethods() {
+ RunActivationMethods<PreApplicationStartMethodAttribute>();
+ }
+
public static void RunPostStartMethods() {
- // Go through all the relevant assemblies and run the PostStart logic
- foreach (var assembly in Assemblies) {
- foreach (PostApplicationStartMethodAttribute postStartAttrib in assembly.GetPostAppStartAttributes()) {
- // Invoke the method that the attribute points to
- postStartAttrib.InvokeMethod();
- }
- }
+ RunActivationMethods<PostApplicationStartMethodAttribute>();
}
- private static void ProcessAppCodeAssemblies() {
- if (BuildManager.CodeAssemblies != null) {
- // Go through all the App_Code assemblies
- foreach (var assembly in BuildManager.CodeAssemblies.OfType<Assembly>()) {
- // Fail if there are any PreStart attribs in App_Code as we can't call them since App_Code is not even compiled before App_Start.
- foreach (PreApplicationStartMethodAttribute preStartAttrib in assembly.GetPreAppStartAttributes()) {
- throw new Exception(String.Format(
- "PreApplicationStartMethodAttribute cannot be used in AppCode (for method {0}.{1}). Please use PostApplicationStartMethodAttribute instead.",
- preStartAttrib.Type.FullName, preStartAttrib.MethodName));
- }
+ public static void RunShutdownMethods() {
+ RunActivationMethods<ApplicationShutdownMethodAttribute>();
+ }
- foreach (PostApplicationStartMethodAttribute postStartAttrib in assembly.GetPostAppStartAttributes()) {
- postStartAttrib.InvokeMethod();
- }
+ // Call the relevant activation method from all assemblies
+ private static void RunActivationMethods<T>() where T : BaseActivationMethodAttribute {
+ foreach (var assembly in Assemblies.Concat(AppCodeAssemblies)) {
+ foreach (BaseActivationMethodAttribute activationAttrib in assembly.GetActivationAttributes<T>()) {
+ activationAttrib.InvokeMethod();
}
}
}
- private static IEnumerable<string> GetAssemblyFiles() {
- // When running under ASP.NET, find assemblies in the bin folder.
- // Outside of ASP.NET, use whatever folder WebActivator itself is in
- string directory = HostingEnvironment.IsHosted
- ? HttpRuntime.BinDirectory
- : Path.GetDirectoryName(typeof(ActivationManager).Assembly.Location);
- return Directory.GetFiles(directory, "*.dll");
- }
-
class StartMethodCallingModule : IHttpModule {
- private static object initLock = new object();
- private static bool hasInited;
+ private static object _lock = new object();
+ private static int _initializedModuleCount;
public void Init(HttpApplication context) {
- // Make sure we only call the methods once per app domain
- lock (initLock) {
- if (!hasInited) {
+ lock (_lock) {
+ // Keep track of the number of modules initialized and
+ // make sure we only call the post start methods once per app domain
+ if (_initializedModuleCount++ == 0) {
RunPostStartMethods();
-
- // Process any attribute found in App_Code.
- ProcessAppCodeAssemblies();
-
- hasInited = true;
}
}
}
public void Dispose() {
+ lock (_lock) {
+ // Call the shutdown methods when the last module is disposed
+ if (--_initializedModuleCount == 0) {
+ RunShutdownMethods();
+ }
+ }
}
}
}
@@ -0,0 +1,11 @@
+using System;
+
+namespace WebActivator {
+ // Same as PreApplicationStartMethodAttribute, but for methods to be called when the app shuts down
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class ApplicationShutdownMethodAttribute : BaseActivationMethodAttribute {
+ public ApplicationShutdownMethodAttribute(Type type, string methodName)
+ : base(type, methodName) {
+ }
+ }
+}
@@ -1,24 +1,14 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Reflection;
namespace WebActivator {
static class AssemblyExtensions {
- public static IEnumerable<PreApplicationStartMethodAttribute> GetPreAppStartAttributes(this Assembly assembly) {
- // Go through all the PreApplicationStartMethodAttribute attributes
- // Note that this is *our* attribute, not the System.Web namesake
-
- return assembly.GetCustomAttributes(
- typeof(PreApplicationStartMethodAttribute),
- inherit: false).OfType<PreApplicationStartMethodAttribute>();
- }
-
- public static IEnumerable<PostApplicationStartMethodAttribute> GetPostAppStartAttributes(this Assembly assembly) {
+ // Return all the attributes of a given type from an assembly
+ public static IEnumerable<T> GetActivationAttributes<T>(this Assembly assembly) where T : BaseActivationMethodAttribute {
return assembly.GetCustomAttributes(
- typeof(PostApplicationStartMethodAttribute),
- inherit: false).OfType<PostApplicationStartMethodAttribute>();
+ typeof(T),
+ inherit: false).OfType<T>();
}
}
}
@@ -2,14 +2,13 @@
using System.Reflection;
namespace WebActivator {
- // This attribute is similar to its System.Web namesake, except that
- // it can be used multiple times on an assembly.
+ // Base class of all the activation attributes
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public abstract class BaseApplicationStartMethodAttribute : Attribute {
+ public abstract class BaseActivationMethodAttribute : Attribute {
private Type _type;
private string _methodName;
- public BaseApplicationStartMethodAttribute(Type type, string methodName) {
+ public BaseActivationMethodAttribute(Type type, string methodName) {
_type = type;
_methodName = methodName;
}
@@ -1,10 +1,9 @@
using System;
-using System.Reflection;
namespace WebActivator {
// Same as PreApplicationStartMethodAttribute, but for methods to be called after App_Start
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public sealed class PostApplicationStartMethodAttribute : BaseApplicationStartMethodAttribute {
+ public sealed class PostApplicationStartMethodAttribute : BaseActivationMethodAttribute {
public PostApplicationStartMethodAttribute(Type type, string methodName)
: base(type, methodName) {
}
@@ -1,11 +1,10 @@
using System;
-using System.Reflection;
namespace WebActivator {
// This attribute is similar to its System.Web namesake, except that
// it can be used multiple times on an assembly.
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
- public sealed class PreApplicationStartMethodAttribute : BaseApplicationStartMethodAttribute {
+ public sealed class PreApplicationStartMethodAttribute : BaseActivationMethodAttribute {
public PreApplicationStartMethodAttribute(Type type, string methodName)
: base(type, methodName) {
}
@@ -44,9 +44,10 @@
<ItemGroup>
<Compile Include="ActivationManager.cs" />
<Compile Include="AssemblyExtensions.cs" />
+ <Compile Include="ApplicationShutdownMethodAttribute.cs" />
<Compile Include="PostApplicationStartMethodAttribute.cs" />
<Compile Include="PreApplicationStartMethodAttribute.cs" />
- <Compile Include="BaseApplicationStartMethodAttribute.cs" />
+ <Compile Include="BaseActivationMethodAttribute.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
@@ -36,5 +36,14 @@ public class WebActivatorUnitTest {
Assert.IsFalse(TestLibrary.MyStartupCode.Start2Called);
Assert.IsTrue(TestLibrary.MyStartupCode.CallMeAfterAppStartCalled);
}
+
+ [TestMethod]
+ public void TestWebActivatorShutdownMethodsGetCalled() {
+ MyStartupCode.CallMeWhenAppEndsCalled = false;
+
+ WebActivator.ActivationManager.RunShutdownMethods();
+
+ Assert.IsTrue(MyStartupCode.CallMeWhenAppEndsCalled);
+ }
}
}

0 comments on commit 586b23d

Please sign in to comment.