ILearnable .Net

November 22, 2010

WebActivator / PreApplicationStartMethod

Filed under: Uncategorized — andreakn @ 07:51

Yesterday I was totally taken aback by the tiny but excellent webactivator project. I totally had a near-magic experience, that’s when I’m almost starting to suspect something works because of tiny magic elves within the .Net runtime “just know what to do” and even though the code doesn’t seem like it’s doing the right things it’s still working.

Let me explain: WebActivator is something you will probably see more and more of the more you start to use modules from NuGet. It is a neat little dll which allows you to specify anywhere in your code that a certain method is to be called on application startup. See this example from Ninject, this class gets added to your solution when installing the ninject package with nuget:


[assembly: WebActivator.PreApplicationStartMethod(typeof(SinsenWeb.Web.AppStart_NinjectMVC3), "Start")]
namespace YourNameSpace {
    public static class AppStart_NinjectMVC3 {
        public static void RegisterServices(IKernel kernel) {
            //kernel.Bind<IThingRepository>().To<SqlThingRepository>();
        }

        public static void Start() {
            // Create Ninject DI Kernel
            IKernel kernel = new StandardKernel();

            // Register services with our Ninject DI Container
            RegisterServices(kernel);

            // Tell ASP.NET MVC 3 to use our Ninject DI Container
            DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
        }
    }

Ignore the ninject specific stuff. The goodness that webactivator brings to the table is the first line where a usage of the webactivator attribute will result in a certain method will be called on startup.

Interested in knowing just *how* WebActivator manages to do this I looked up the sourcecode. Latest version of  which is available here

as it only consists of two classes, the attribute which you can include (like ninject did) and the implementation of the logic I thought it would be easy to get a handle on this.

I’ll include the two sources here so you can see if you’re smarter than me in figuring out how this works.
Attribute:

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 : Attribute {
        private Type _type;
        private string _methodName;

        public PreApplicationStartMethodAttribute(Type type, string methodName) {
            _type = type;
            _methodName = methodName;
        }

        public Type Type {
            get {
                return _type;
            }
        }

        public string MethodName {
            get {
                return _methodName;
            }
        }

        public void InvokeMethod() {
            // Get the method
            MethodInfo method = Type.GetMethod(
                MethodName,
                BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

            if (method == null) {
                throw new ArgumentException(
                    String.Format("The type {0} doesn't have a static method named {1}",
                        Type, MethodName));
            }

            // Invoke it
            method.Invoke(null, null);
        }
    }
}

Implementation:


using System;
using System.IO;
using System.Reflection;
using System.Web;

namespace WebActivator {
    public class PreApplicationStartCode {
        public static void Start() {
            // Go through all the bin assemblies
            foreach (var assemblyFile in Directory.GetFiles(HttpRuntime.BinDirectory, "*.dll")) {
                var assembly = Assembly.LoadFrom(assemblyFile);

                // Go through all the PreApplicationStartMethodAttribute attributes
                // Note that this is *our* attribute, not the System.Web namesake
                foreach (PreApplicationStartMethodAttribute preStartAttrib in assembly.GetCustomAttributes(
                    typeof(PreApplicationStartMethodAttribute),
                    inherit: false)) {

                    // Invoke the method that the attribute points to
                    preStartAttrib.InvokeMethod();
                }
            }
        }
    }
}

Hmm, so basically, we declare an attribute and we have a static method which when run will track down all instances of this attribute in all dlls in the bin folder and fire the method as specified in each of those instances.

But *who* calls the static method?

I had a look in the Properties\AssemblyInfo.cs file which looks like this:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.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("WebActivator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("WebActivator")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
[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("3bc078bd-ade4-4271-964f-1d041508c419")]

// 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(WebActivator.PreApplicationStartCode), "Start")]

and in the bottom of that file I finally started to believe that magic little elves live within the jit-compiler and make everything fine and dandy even though it seems like the feature is using itself to start itself.

Maybe there really *are* turtles all the way down?

Have you spotted how it works yet?

I’ll give you some whitespace to think of the solution before I blurt it out and ruin your fun

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


WebActivator.PreApplicationStartMethod != System.Web.PreApplicationStartMethod

WebActivator doesn’t use itself but rather a feature of the Asp.Net 4.0 runtime to start itself off. The whole motivation for WebActivator is that the feature built into Asp.Net 4.0 has a design flaw in that it only allows a single instance of the attribute within each dll, making it hard to use for NuGet scenarios where you would want to use multiple different packages each with their own startup logic independent of eachother, you would have to manually merge the logic into one place if you wanted to use the Asp.Net 4.0 implementation.

However, since WebActivator.dll is a tiny separate dll it can make use of the asp.net feature to kickstart the other dlls.

I think that I would prefer if the AssemblyInfo.cs file was a little more specific in its usage of the asp.net feature, seeing that it’s basically reimplementing it itself. Instead of relying on a using statement for System.Web, I would prefer if the attribute was declared as

[assembly: System.Web.PreApplicationStartMethod(typeof(WebActivator.PreApplicationStartCode), "Start")]

 

About these ads

3 Comments »

  1. Yay, I was also befuddled by this. Thanks for doing the legwork.

    Comment by Nate G. — February 18, 2012 @ 21:23 | Reply

  2. […] med att överlagra application_start och request som beskrivet på deras sida eller använda webactivator för att starta […]

    Pingback by Ha kontroll på din sidladdning med MVC miniprofiler « EnovaLeaks — November 15, 2012 @ 15:43 | Reply

  3. […] I was looking up more about webactivator, I found this link which does a really good job of discussing about webactivator. But, I felt a few things could […]

    Pingback by Performing Activities Before an Application Starts Using the PreApplicationStartMethod Attribute | the dotNet way!!! — January 16, 2013 @ 04:23 | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: