views:

131

answers:

2

Strange thing happening to me with the PreApplicationStartMethod Attribute. I did implement it in my latest project. In the AssemblyInfo.cs I have the following line:

[assembly: PreApplicationStartMethod(typeof(MyAssembly.Initializer), "Initialize")]

The Type and method look like this:

namespace MyAssembly
{
    public static class Initializer
    {
       public static void Initialize()
       {
           TranslationKeys.Initialize();
       }
    }
}

When I rebuild my application and load it in the browser I get the following error:

The method specified by the PreApplicationStartMethodAttribute on assembly 'MyWebApp, Version=0.0.1.0, Culture=neutral, PublicKeyToken=null' cannot be resolved. Type: 'MyAssembly.Initializer', MethodName: 'Initialize'. Verify that the type is public and the method is public and static (Shared in Visual Basic).

I really have no idea what the problem is.

A: 

I copied your code and it works perfectly. So try to clean and rebuild the whole application.

Are you sure you are not in a nested namespace or something?

Snake
The namespace of my web application is WebApplication. The namespace of the Refresher is WebApplication.Logic. That shouldn't make any difference, right?
Joop
I did clean and rebuild my whole solution. I am running it on my development machine. Windows XP with IIS 5.1. But since it is targeting .NET 4.0 that also shouldn't make a difference.
Joop
And if you try with the built in webserver (Casini)
Snake
The same exception occurs. Strange.
Joop
+4  A: 

Strange, we use this feature a lot in the ASP.NET team, and have not run into this. To help debug this, can you try running the following code, which does something similar to what ASP.NET does to locate the method?

The best way to run it is to create a Console app and put that code in there. Then just call it, passing it the assembly where you are seeing the issue. You'll then want to debug it and trace through it carefully to see what goes on.

BTW, before doing this, double check that you are putting the attribute on the same assembly that contains the class. i.e. it can't point to a type in a different assembly.

Here is the code to try:

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

public class TestClass {
    public static void TestPreStartInitMethodLocation(Assembly assembly) {
        var attributes = (PreApplicationStartMethodAttribute[])assembly.GetCustomAttributes(typeof(PreApplicationStartMethodAttribute), inherit: true);

        if (attributes != null && attributes.Length != 0) {
            PreApplicationStartMethodAttribute attribute = attributes[0];

            MethodInfo method = null;
            // They must be in the same assembly!
            if (attribute.Type != null && !String.IsNullOrEmpty(attribute.MethodName) && attribute.Type.Assembly == assembly) {
                method = FindPreStartInitMethod(attribute.Type, attribute.MethodName);
            }

            if (method == null) {
                throw new HttpException("Couldn't find attribute");
            }
        }
    }

    public static MethodInfo FindPreStartInitMethod(Type type, string methodName) {
        MethodInfo method = null;
        if (type.IsPublic) {
            method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
                            binder: null,
                            types: Type.EmptyTypes,
                            modifiers: null);
        }
        return method;
    }
}
David Ebbo
BTW, before doing this, double check that you are putting the attribute on the same assembly that contains the class. i.e. it can't point to a type in a different assembly.---------------------------------------Thank you! That did the trick. In my solutions I have different projects. The Initialize class was in a different solution than my web application.
Joop
However. Thinking about it and looking at the stacktrace I think it's a weird thing the class needs to be in the same project/assembly. It says: System.Web.Compilation.BuildManager.GetPreStartInitMethodsFromReferencedAssemblies(). And the assembly that contains my Initialize class is referenced.
Joop
Don't make too much of the name of internal methods :) Here, what it's saying is that it looks for the Init methods for each of the referenced assemblies. But for a given assembly, it only looks in that assembly itself. This is a delibarate check that we added to prevent an assembly from causing unrelated code in a different assembly to be called. The logic that we follow is basically what I wrote above, so the same-assembly check is entirely deliberate.
David Ebbo