Почему создание Application_Start статическим методом (рекомендация FxCopAnalyzer) вызывает 404 ошибки? - PullRequest
1 голос
/ 28 октября 2019

Visual Studio рекомендует установить новые Microsoft.CodeAnalysis.FxCopAnalyzers в моем проекте. Он хорошо идентифицирует ряд улучшений в источнике, но одна из его рекомендаций такова:

CA1822: Member Application_Start does not access instance data and can be marked as static (Shared in `VisualBasic`).

Я проверил процедуру и, конечно же, FxCopAnalyzer был прав. Поэтому я изменил:

protected void Application_Start()

на

protected static void Application_Start()

Однако, когда я делаю это изменение, я получаю следующее:

Ошибка HTTP 403.14 - Запрещено

Веб-сервер настроен так, чтобы не перечислять содержимое этого каталога.

Если вынуть «статический», он снова будет работать. Но меня немного смущает, почему фреймворк заботится о том, чтобы этот метод был статическим.

Ответы [ 2 ]

0 голосов
/ 28 октября 2019

Это был такой интересный вопрос. Мне потребовалось некоторое время, чтобы понять конвейер начальной загрузки Asp.Net. Я не буду вдаваться в подробности, потому что это займет много времени, поэтому я оставлю детали для OP.

По сути, среда Asp.Net динамически создает сборку и создает динамически создаваемый тип, который наследует ваш * 1003. *.

Итак, вот значение по умолчанию MvcApplication

public class MvcApplication : HttpApplication {

    public static void Application_Start() {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Итак, что я подразумеваю под средой Asp.Net, динамически создавая другую сборку и тип? Вы заметите, что, проверив следующий код → Давайте изменим метод Application_Start:

public static void Application_Start() {
    var whatIsMyType =  GetType();
    //You will see that our actual type is of ASP.global_asax,
    //which inherits  MvcApplication, which inherits  HttpApplication      
    //Other Stuff...
}

Где создается тип ASP.global_asax? Вам нужно будет углубиться в исходный код, но позвольте мне дать вам подсказку

Так как же выглядит настоящий ASP.global_asax?

[System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
public class global_asax : global::<YourNameSpace>.MvcApplication {

    private static bool @__initialized;

    [System.Diagnostics.DebuggerNonUserCodeAttribute()]
    public global_asax() {
        if ((global::ASP.global_asax.@__initialized == false)) {
            global::ASP.global_asax.@__initialized = true;
        }
    }

    protected System.Web.Profile.DefaultProfile Profile {
        get {
            return ((System.Web.Profile.DefaultProfile)(this.Context.Profile));
        }
    }
}

Наконец,мы можем перейти к реальному ответу: почему из-за статического Application_Start приложение ведет себя неожиданно?

В HttpApplicationFactory классе, который используется при начальной загрузке вашего приложения, есть следующая строкакода

methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
    if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
        handlers.Add(m);
}

Загадка решена в _theApplicationType. Пожалуйста, помните, что это ASP.global_asax type. Ваш статический метод Application_Start определен в типе MvcApplication, поэтому отражение не найдет его через _theApplicationType, и в результате он не будет назначен для вызова при запуске приложения.

Вот простой кодчтобы проверить это.

public class BaseClass {
    public static void StaticMethodInBaseClass() {
    }
}

public class DerivedClass {
    public void DerivedClassMethod() {
    }
}

//You will not get `StaticMethodInBaseClass` here
var methods = typeof(DerivedClass).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
0 голосов
/ 28 октября 2019

Создание Application_Start() статического делает его , а не частью конвейера ASP.NET. Создание метода static изменяет сигнатуру метода, и среда больше не может найти метод, который ищет.

Однако ...

Я не понимаю, почему он не вызывается, он должен.

Вот части HttpApplicationFactory , которые показывают, что этоон ищет методы BindingFlags.Instance и Binding.Static.

methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (MethodInfo m in methods) {
    if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
        handlers.Add(m);
}

Затем метод вызывается для объекта HttpApplication, поскольку метод теперь статический, первый параметр следует игнорировать и вызывать статический метод.

if (paramCount == 0) {
   method.Invoke(this, new Object[0]);
}

Почему 404/403?

Application_Start - это, как правило, место, где маршруты настроены .

My toy app

Я собрал игрушку, чтобы устранить все очевидное. Метод называется.

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

namespace NETFrameworkConsoleApp2
{
    public class MyHttpApp : HttpApplication
    {
        protected static void Application_Start()
        {
            Console.WriteLine("Very important work");
        }
    }

    class Program
    {
        private MethodInfo _onStartMethod;        // Application_OnStart

        public static void Main()
        {
            //Flags from https://referencesource.microsoft.com/#System.Web/HttpApplicationFactory.cs,74e5273062f54e5f,references
            var methods = typeof(MyHttpApp).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

            var a = new MyHttpApp();

            var p = new Program();

            foreach (MethodInfo m in methods)
            {
                p.ReflectOnMethodInfoIfItLooksLikeEventHandler(m);
            }

            p._onStartMethod.Invoke(a, new Object[0]);
            Console.ReadLine();
        }


        private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
        {
            // From https://referencesource.microsoft.com/#System.Web/HttpApplicationFactory.cs,b0a90d9df37ace19,references
            if (m.ReturnType != typeof(void))
                return false;

            // has to have either no args or two args (object, eventargs)
            ParameterInfo[] parameters = m.GetParameters();

            switch (parameters.Length)
            {
                case 0:
                    // ok
                    break;
                case 2:
                    // param 0 must be object
                    if (parameters[0].ParameterType != typeof(System.Object))
                        return false;
                    // param 1 must be eventargs
                    if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                        !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                        return false;
                    // ok
                    break;

                default:
                    return false;
            }

            // check the name (has to have _ not as first or last char)
            String name = m.Name;
            int j = name.IndexOf('_');
            if (j <= 0 || j > name.Length - 1)
                return false;

            // special pseudo-events
            if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
                StringUtil.EqualsIgnoreCase(name, "Application_Start"))
            {
                _onStartMethod = m;
                //_onStartParamCount = parameters.Length;
            }
            else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Application_End"))
            {
                //_onEndMethod = m;
                //_onEndParamCount = parameters.Length;
            }
            else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                     StringUtil.EqualsIgnoreCase(name, "Session_End"))
            {
                //_sessionOnEndMethod = m;
                //_sessionOnEndParamCount = parameters.Length;
            }

            return true;
        }

        internal static class StringUtil
        {
            //From https://referencesource.microsoft.com/#System.Web/Util/StringUtil.cs,d3a0b2a26cb3f1e1
            internal static bool EqualsIgnoreCase(string s1, string s2)
            {
                if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2))
                {
                    return true;
                }
                if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2))
                {
                    return false;
                }
                if (s2.Length != s1.Length)
                {
                    return false;
                }
                return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
            }
        }

        static Program() => Console.WriteLine(GetFrameworkName());

        static string GetFrameworkName()
            => ((System.Runtime.Versioning.TargetFrameworkAttribute)
                    (System.Reflection.Assembly.GetEntryAssembly()
                    .GetCustomAttributes(typeof(System.Runtime.Versioning.TargetFrameworkAttribute), true)[0]))
                    .FrameworkName; // Example: .NETCoreApp,Version=v3.0
    }
}
...