Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does PreApplicationStartMethodAttribute() not work in SharePoint 2013?

In .NET 4(.5) there is this amazing Attribute: PreApplicationStartMethodAttribute, I want to use it in SharePoint 2013 so I don't have to edit the app_start method in the global.asax file directly.

Since SP2013 runs the correct version of .NET I assumed that this attribute would just work... but this seems not to be the case.

Has anyone figured out how to use it yet? or explain why it cannot work?

Small update: In the system.web dll I can see PreApplicationStartMethodAttribute is called by the folowing class.

// System.Web.Compilation.BuildManager
internal static ICollection<MethodInfo> GetPreStartInitMethodsFromAssemblyCollection(IEnumerable<Assembly> assemblies, bool buildingFromCache)
{
    List<MethodInfo> list = new List<MethodInfo>();
    foreach (Assembly current in assemblies)
    {
        PreApplicationStartMethodAttribute[] array = null;
        try
        {
            array = (PreApplicationStartMethodAttribute[])current.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), true);
        }
        catch
        {
        }
        if (array == null || !array.Any<PreApplicationStartMethodAttribute>())
        {
            if (buildingFromCache)
            {
                return null;
            }
        }
        else
        {
            PreApplicationStartMethodAttribute[] array2 = array;
            for (int i = 0; i < array2.Length; i++)
            {
                PreApplicationStartMethodAttribute preApplicationStartMethodAttribute = array2[i];
                MethodInfo methodInfo = null;
                if (preApplicationStartMethodAttribute.Type != null && !string.IsNullOrEmpty(preApplicationStartMethodAttribute.MethodName) && preApplicationStartMethodAttribute.Type.Assembly == current)
                {
                    methodInfo = BuildManager.FindPreStartInitMethod(preApplicationStartMethodAttribute.Type, preApplicationStartMethodAttribute.MethodName);
                }
                if (!(methodInfo != null))
                {
                    throw new HttpException(SR.GetString("Invalid_PreApplicationStartMethodAttribute_value", new object[]
                    {
                        current.FullName,
                        (preApplicationStartMethodAttribute.Type != null) ? preApplicationStartMethodAttribute.Type.FullName : string.Empty,
                        preApplicationStartMethodAttribute.MethodName
                    }));
                }
                list.Add(methodInfo);
            }
        }
    }
    return list;
}

If SharePoint 2013 is running on asp.net 4.0 I might have some luck invoking the following on the applicaton.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe
like image 717
Elliot Wood Avatar asked Jan 17 '13 08:01

Elliot Wood


1 Answers

The following is just speculation but based on experimentation.

I think SharePoint 2013 uses some sort of runtime assembly loading to load any and all custom code assemblies. After all, the app doesn't recompile when we add a WebPart. A simple means of how this could be done is described here: http://msdn.microsoft.com/en-us/library/d133hta4.aspx

I created a simple ASP MVC app where I tried this precise technique, and loaded an assembly in my global.asax:

 protected void Application_Start()
    {
        ObjectHandle handle = Activator.CreateInstance("ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=27b9c31f70a4bee3", "ClassLibrary1.Class1");
        Object p = handle.Unwrap();
        Type t = p.GetType();

        MethodInfo method = t.GetMethod("ToString");
        Object retVal = method.Invoke(p, null);
    }

The assembly being loaded is something I created and placed in the GAC. This ClassLibrary1 is really barebone, with a class Class1 that contains one method ToString(). The ToString method throws an exception, easy to see if it got called:

public class Class1
{
    public override string ToString()
    {
        throw new ApplicationException("Joe was in the tostring");
        return base.ToString();
    }
}

The assembly also uses the PreApplicationStartMethod in the AssemblyInfo.cs file:

[assembly: PreApplicationStartMethod(typeof(ClassLibrary1.JoeAssemblyStart), "Start")]

Lastly there is a simple class that is called by the PreApplicationStartMethod attribute:

 public class JoeAssemblyStart
{
    public static void Start()
    {
        throw new ApplicationException("joe was here in this assembly start");
    }
}

The interesting things happen at runtime. When I run this code, the exception that is thrown is from the ToString in Class1, not the JoeAssemblyStart class. This means that the PreApplicationStartMethod attribute is ignored when we load an assembly at runtime, and this is not surprising when thinking of the ASP.NET pipeline.

I also ran a version of this experiment when I added the ClassLibrary1 to my MVC project explicitly and did not load it dynamically. In this case, the PreApplicationStartMethod code got called as expected.

So to sum it up:

  1. SharePoint 2013 is not rebuilt when you add code to it
  2. Therefore your code has to be loaded in some dynamic way
  3. Assemblies loaded dynamically cannot influence the ASP.NET pipeline as early as the PreApplicationStartMethod
like image 172
Joe Capka Avatar answered Sep 27 '22 23:09

Joe Capka