ASP.NET MVC2 + Ninject + NLog (+ shared hosting?) = NullReferenceException ASP.NET MVC2、Ninject、NLog(+共享主机?)=空引用异常

15

我有一个基于Tekpub Starter Site的MVC2应用程序,因此它使用Ninject进行依赖注入,使用NLog进行日志记录,并在各个位置使用其他库。但据我所知,正是这些库导致了我的问题。

在使用ASP.NET开发服务器(Cassini)时,我的PC上一切都运作良好,但当我部署到服务器(它是一个廉价的共享托管交易)时,我会遇到一个看起来与Ninject实例化日志记录器有关的NullReferenceException异常。

这是我的Global.asax.cs文件中相关的部分:

protected override void OnApplicationStarted() {
    Logger.Info("App is starting"); // <-- I think this is what's causing the problem

    RegisterRoutes(RouteTable.Routes);
    //MvcContrib.Routing.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
    RegisterAllControllersIn(Assembly.GetExecutingAssembly());
    SetEngines();
}

protected override IKernel CreateKernel() {
    return Container;
}

internal class SiteModule : NinjectModule {
    public override void Load() {
        Bind<ILogger>().To<NLogLogger>();
        // and a couple of others...
    }
}

protected void Application_End() {
    Logger.Info("App is shutting down");
}

protected void Application_Error() {
    Exception lastException = Server.GetLastError();
    Logger.Fatal(lastException);
}

public ILogger Logger {
    get {
        return Container.Get<ILogger>();
        }
    }
    static IKernel _container;
    public static IKernel Container {
        get {
            if (_container == null) {
                _container = new StandardKernel(new SiteModule());
            }
            return _container;
        }
    }

nlog.config

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >

    <targets>
        <!--Useful for debugging-->
        <target name="console" xsi:type="ColoredConsole"
         layout="${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}" />

        <target name="file" xsi:type="File" fileName="${basedir}/App_Data/Site.log"
         layout="${date}: ${message}" />

        <target name="eventlog" xsi:type="EventLog" source="My App" log="Application"
        layout="${date}: ${message} ${stacktrace}" />

    </targets>

    <rules>
        <logger name="*" minlevel="Info" writeTo="file" />
        <!-- <logger name="*" minlevel="Fatal" writeTo="eventlog" /> -->
    </rules>
</nlog>

NLogLogger类:

public class NLogLogger:ILogger {

    private Logger _logger;

    public NLogLogger() {
        _logger = LogManager.GetCurrentClassLogger();
    }

    public void Info(string message) {
        _logger.Info(message);
    }

    // similar for Warn, Debug, etc

堆栈跟踪:

[NullReferenceException: Object reference not set to an instance of an object.]
   NLog.LogManager.GetCurrentClassLogger() +84
   DynamicInjector8cdfc2eb02f8497596a4704e379a4bb4(Object[] ) +40
   Ninject.Activation.Providers.StandardProvider.Create(IContext context) +319
   Ninject.Activation.Context.Resolve() +182
   Ninject.KernelBase.<Resolve>b__4(IContext context) +8
   System.Linq.<>c__DisplayClass12`3.<CombineSelectors>b__11(TSource x) +32
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +151
   System.Linq.<CastIterator>d__b1`1.MoveNext() +92
   System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source) +4187840
   Ninject.ResolutionExtensions.Get(IResolutionRoot root, IParameter[] parameters) +149
   Web.Application.get_Logger() in C:\mvcstarter\Web\Global.asax.cs:159
   Web.Application.OnApplicationStarted() in C:\mvcstarter\Web\Global.asax.cs:80
   Ninject.Web.Mvc.NinjectHttpApplication.Application_Start() +535

[HttpException (0x80004005): Object reference not set to an instance of an object.]
   System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9024793
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +131
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +194
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +339
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +253

[HttpException (0x80004005): Object reference not set to an instance of an object.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +8946484
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +97
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +256
如果我在OnApplicationStarted中注释掉日志记录,我会得到一个不同的异常。我不确定为什么会发生这种情况,因为不应该有任何日志记录,所以我不确定为什么它正在尝试实例化记录器。
[NullReferenceException: Object reference not set to an instance of an object.]
   NLog.LogManager.GetCurrentClassLogger() +84
   DynamicInjector5ca7d21cf56b4732957e22cb1bfd8bdd(Object[] ) +40
   Ninject.Activation.Providers.StandardProvider.Create(IContext context) +319
   Ninject.Activation.Context.Resolve() +182
   Ninject.KernelBase.<Resolve>b__4(IContext context) +8
   System.Linq.<>c__DisplayClass12`3.<CombineSelectors>b__11(TSource x) +32
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +151
   System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source) +4187840
   Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent) +347
   Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) +138
   Ninject.Activation.Providers.<>c__DisplayClass2.<Create>b__1(ITarget target) +17
   System.Linq.WhereSelectArrayIterator`2.MoveNext() +85
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +325
   System.Linq.Enumerable.ToArray(IEnumerable`1 source) +78
   Ninject.Activation.Providers.StandardProvider.Create(IContext context) +306
   Ninject.Activation.Context.Resolve() +182
   Ninject.KernelBase.<Resolve>b__4(IContext context) +8
   System.Linq.<>c__DisplayClass12`3.<CombineSelectors>b__11(TSource x) +32
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +151
   System.Linq.<CastIterator>d__b1`1.MoveNext() +92
   System.Linq.Enumerable.FirstOrDefault(IEnumerable`1 source) +4187840
   Ninject.ResolutionExtensions.TryGet(IResolutionRoot root, String name, IParameter[] parameters) +261
   Ninject.Web.Mvc.NinjectControllerFactory.CreateController(RequestContext requestContext, String controllerName) +108
   System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory) +124
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +50
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
   System.Web.Mvc.<>c__DisplayClass7.<BeginProcessRequest>b__6() +29
   System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Func`1 func) +38
   System.Web.Mvc.ServerExecuteHttpHandlerAsyncWrapper.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +97
   System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride) +1508
   System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage) +77
   System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +28
   System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +22
   System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter) +372
   System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper htmlHelper, String actionName, String controllerName) +35
   ASP.views_home_index_aspx.__RenderContent2(HtmlTextWriter __w, Control parameterContainer) in c:\HostingSpaces\<snip>\wwwroot\Views\Home\Index.aspx:8
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
   System.Web.UI.Control.Render(HtmlTextWriter writer) +10
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   ASP.views_shared_site_master.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in c:\HostingSpaces\<snip>\wwwroot\Views\Shared\Site.Master:48
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
   System.Web.UI.Control.Render(HtmlTextWriter writer) +10
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
   System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +55
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3060

我唯一能让网站工作的方法就是完全模拟日志记录器,但我对此并不满意。这是我第一次使用ASP.NET共享主机,所以我有点茫然。有人有任何有用的建议吗?


下面的答案帮助我解决了在WPF中类似的问题(请将其标记为答案!)。然而,对于Web开发,如果您只是从本地在IIS上进行测试,而不是使用Cassini,则可以避免奇怪的调试问题 - 无论如何,尽可能使您的开发/测试/生产环境相似可以在长期内减少烦恼。 - Ted
下面的答案也解决了我的问题:NLog + Ninject 在 Debug 模式下工作正常,但在 Release 模式下不起作用。 - Dariusz
4个回答

35

你可以避免使用 LogManager.GetCurrentClassLogger() 方法,让 Ninject 自动解析当前类名:

将 NLogLogger 类的构造函数更改为以下内容:

public NLogLogger(string currentClassName) 
{
  _logger = LogManager.GetLogger(currentClassName);
}

将Ninject绑定更改为解析当前类名:

Bind<ILogger>().To<NLogLogger>()
  .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName);

额外的好处是:现在您的日志文件还包括发出日志指令的类名。

我知道我的答案有点晚了,但我今天自己也在苦苦挣扎,无法找到答案。也许这能帮助其他人。


你的回答帮助我找到了一个StructureMap解决方案:https://dev59.com/o2855IYBdhLWcg3wIAka#4525456 - Petrus Theron
非常感谢你,Robin。这真的很有帮助。我已经浪费了将近一天的时间,试图找出我只在发布模式下遇到的错误的原因。 - Eranga Dissanayaka
谢谢。我也只在发布版本中遇到了这个错误。 - Shawn de Wet

5

1

共享主机通常对反射等有限制。

所以我的猜测是

[NullReferenceException: 对象引用未设置为对象的实例。] NLog.LogManager.GetCurrentClassLogger() +84

与此有关 - 如果您使用一种机制获取记录器,其中您通过 typeof 在编译时获取一个 Type,会发生什么?


我选择这个主机的原因之一(除了价格低廉 - 非营利组织和其他方面)是他们据说没有任何限制。虽然我并不是NLog大师,但我的理解是你引用的方法是使用它的方式,例如请参阅http://nlog-project.org/wiki/Tutorial#Creating_loggers...我回家后会仔细研究一下... - Jon
@Jon:和全球其他人一样,我也使用过我的日志记录器(并且出于我的耻辱写了一些)。我刚刚使用了log4net和.NET上的内置工具,而不是NLog-但是我对它如何与lo4net一起工作有深入的了解,它们都是相似的。看着引用的文章,他们有一个需要传递名称的,还有一个不需要传递名称的。不需要传递名称的那个必须以某种方式确定名称-这种方式就是反射。在共享主机中,某些反射(不是全部)受到限制。因此,从方程式中删除GetCurrentClassLogger可以将此可能原因从您的调查中消除。 - Ruben Bartelink

0

我曾经遇到过类似的问题。

我的(简单)解决方案:

替换这些行

private Logger Logger = LogManager.GetCurrentClassLogger();

通过这行代码

private static Logger Logger = LogManager.GetCurrentClassLogger();

readonly 关键字是可选的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接