【问题标题】:Why does making Application_Start a static method (a recommendation of FxCopAnalyzer) cause 404 errors?为什么将 Application_Start 设为静态方法(FxCopAnalyzer 的建议)会导致 404 错误?
【发布时间】:2020-02-23 09:06:35
【问题描述】:

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 - 禁止

Web 服务器配置为不列出此目录的内容。

如果我取出“静态”,它会再次起作用。但是我有点困惑为什么框架会关心这个方法是否是静态的。

【问题讨论】:

  • 为什么?因为很多时候这是可以接受的。为什么您的程序会中断,因为显然Application_Start 不是其中一种情况,并且不能是静态的。您将需要“禁止代码分析警告”。
  • 我已经发现它不适用于那里的静态,所以当你回答“因为它不起作用”时,这并不能真正解决它为什么不起作用的问题。

标签: c# .net-4.7.2


【解决方案1】:

Application_Start() 设为静态使其成为 ASP.NET 管道的一部分。使方法static 更改方法的签名,框架不再能够找到它正在寻找的方法。

但是...

我不明白为什么它不被调用,它应该被调用。

这是HttpApplicationFactory 的部分内容,表明它会查找BindingFlags.InstanceBinding.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 按照惯例是routes are configured 所在的位置。

我的玩具应用

我拼凑了一个玩具来消除任何明显的东西。该方法被调用。

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
    }
}

【讨论】:

  • 现在这个答案对我来说似乎是正确的。通过使方法静态,例程必须像这样调用:ClassName.Application_Start() 而不是像这样的 InstanceVariable.Application_Start(),并且框架不会那样做。是的......因为在那里定义了路线,这可以解释 404 错误。非常感谢,Tymtam。
【解决方案2】:

这是一个非常有趣的问题。我花了一段时间才了解 Asp.Net 引导管道。我不会详细介绍,因为这会花费很多,所以我将把细节留给 OP。

基本上,Asp.Net 框架会动态创建一个程序集并创建一个动态创建的类型,该类型继承您的MvcApplication

所以这里是默认的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 类型是在哪里创建的?你需要深入挖掘源代码,但让我给你一个hint

那么实际的 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);

【讨论】:

  • 哈桑,对于一个非常困难的问题,这是一个非常出色的答案。我很惊讶你能很好地解决这个问题。后续问题:CA1822 警告和推荐的修复似乎破坏了所有 .Net Framework 应用程序。当然……我可以抑制该警告,但我会尝试解决警告而不是抑制它们。我想知道......即使微软不得不求助于粗暴的硬编码,他们是否应该对所有 .Net Framework web 项目取消这个警告?另外......这是一个相关问题:stackoverflow.com/questions/12360985/…
  • @Glenn,非常感谢,我也学到了很多东西!至于你的CA1822,你应该记住,这些只是建议,在应用这些建议时应该小心。您应该记住,ASP.Net 框架对您的代码有这样的假设,而 FxCop 不会更好地知道。我建议取消此警告作为正确的处理方式。
猜你喜欢
  • 2013-11-17
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-10
相关资源
最近更新 更多