6

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
Elliot Wood
  • 964
  • 1
  • 9
  • 29

4 Answers4

2

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
Joe Capka
  • 562
  • 4
  • 17
  • I think you're definitely on the right track here, but I am really trying to locate the specific event in SharePoint where this occurs(or does not occur and why?). If this ASP.net mechanism is bypassed then Id like to know at what point this *Should* occur. P.s. Thanks to you answering my other question I believe I can make some progress on this question. – Elliot Wood Apr 18 '13 at 00:37
  • 1
    I think the crux of it is that in order to get the PreApplicationStartMethod to work, the application needs to have an explicit reference to the assembly, and this requires a recompile. You can't do that with SP, so it won't work. – Joe Capka Apr 18 '13 at 11:32
2

This is a little late but i think it is important.

The PreApplicationStartMethod attribute works just fine in SharePoint 2013. However, the assembly needs to be deployed to the bin folder.

Steps

  1. Create your normal SharePoint solution (lets call it SharePointProject).
  2. Create a new Class Library project and give it a strong name. (lets call it ClassProject)
  3. Add the code a new class called Startup to ClassProject.
  4. Add a static method called Start to Startup and do your startup stuff.
  5. Add PreApplicationStartMethod point to Startup attribute the ClassProject.
  6. Go to Package/Advanced settings in the SharePointProject and add ClassProject as assembly that needs to be deployed. Make sure you select WebApplication rather than gac.
  7. Deploy your solution.

This process will deploy the ClassProject to the bin folder of your SharePoint web application. The start method will be called by the runtime and everything should work. It is important to ensure you deploy ClassProject to the WebApplication rather than the GAC.

If you have been playing around with this for a while and it does not work. Try deleting everything under Temporary Internet Files as IIS likes to cache Dlls and your code will not be called.

Oh, and the reason for using ClassProject is because you cannot use Feature Receivers if the SharePoint project is deployed to bin rather than the GAC.

thaoula
  • 21
  • 3
1

I have noticed some projects dont have a "PreApplicationStartup" i ran into similar problems using this on a console application, though it worked fine on a website.

It could also be that the method you are running is not a static method.

public class PreApplicationStart
    {
        public static void Start()
        {
            // Pre application startup configuration goes here
        }
    }

Add the following in AssemblyInfo.cs

[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")]
Jim Wolff
  • 5,052
  • 5
  • 34
  • 44
  • Good thought but it definitely is running as a static method. It just seems to not get called, so i'm really trying to find out why. Any suggestions on how to debug something like this would be really appreciated also. – Elliot Wood Feb 01 '13 at 02:49
  • I remember sitting in a similar situation, in the end i came to the conclusion that it was because i was trying to do it on a non-website project, and solved my issue in a different way, not entirely sure what your issue here is. – Jim Wolff Feb 05 '13 at 13:41
1

I tried several approaches - the only way I got it working was with a httpmodule: http://melcher.it/2012/12/signalr-in-sharepoint-2013-the-real-time-web-is-coming/

I think the reason why PreApplicationStart does not work is because the global.asax inherits SPHttpApplication and not HttpApplication - had not time to go through the sources.

So long, Max

Max Melcher
  • 200
  • 1
  • 12
  • Not sure the SPHttpApplication has anything to do with it, since the PreApplicationStart stuff happens before the global.asax is triggered. Create a small MVC app and place breakpoints in the two spots to test. – Joe Capka Apr 16 '13 at 19:07