1

Hi I am trying to use NLog with an Web Api 2 application that uses Ninject for dependency injection.

I have struggled to find any concrete info on how to do this so far I have installed the NuGet packages for NLog & NLog.Configuration. So far what I have read suggests that theses packages should just work and take care of the binding for me.

I set up the Nlog.config file and then I installed the Ninject.Extensions.Logging & Ninject.Extensions.NLog4 packages.

I'm trying to use NLog as follows

public class ErrorCheckController : ApiController
{
    private readonly ILogger _logger;

    public ErrorCheckController(ILogger logger)
    {
        _logger = logger;
    }

    public IHttpActionResult Get(string version)
    {
        try
        {
            throw new Exception("Manually thrown exception");
        }
        catch (Exception ex)
        {
            _logger.Debug(ex);
        }

        return Ok();
    }
}

In my NinjectWebCommon I have tried

  1. No Binding
  2. Binding with kernel.Bind<ILogger>().To<Logger>();
  3. Binding with kernel.Bind<ILogger>().ToMethod(lm => LogManager.GetLogger("MyLogger")); where MyLogger is the name of the target configuration

When I do 1 & 2 I get the error

An error occurred when trying to create a controller of type 'ErrorCheckController'. Make sure that the controller has a parameterless public constructor.

The 3rd option gets a Logger but it isn't configured using the NLog.config file

EDIT Here is my config file

<?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"
  xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
  autoReload="true"
  throwExceptions="false">

  <targets>
    <target xsi:type="Database" name="Ink3DPowderDbLogger" connectionStringName="MyDbContext">
      <commandText>
        INSERT INTO Log.ApiLog (
    Application,
    Logger,
    Logged,
    Level,
    Environment,
    Request,
    Message,
    BaseDir,
    Message,
    CallSite,
    CallSiteLn,
    Exception,
    EventProperties,
    StackTrace,
    ServerName,
    Port,
    Url,
    Https,
    ServerAddress,
    RemoteAddress,
    ) VALUES (
    @Application,
    @Logger,
    @Logged,
    @Level,
    @Environment,
    @Request,
    @BaseDir,
    @Message,
    @CallSite,
    @CallSiteLine,
    @Exception,
    @EventProperties,
    @StackTrace,
    @ServerName,
    @Port,
    @Url,
    @Https,
    @ServerAddress,
    @RemoteAddress,
    );
  </commandText>

  <parameter name="@application" layout="${appsetting:name=AppName:default=Unknown}" />
  <parameter name="@logger" layout="${logger}" />
  <parameter name="@logged" layout="${date}" />
  <parameter name="@level" layout="${level}" />
  <parameter name="@environment" layout="${environment}" />
  <parameter name="@request" layout="${asp-request}" />
  <parameter name="@basedir" layout="${basedir}" />
  <parameter name="@message" layout="${message}" />
  <parameter name="@callSite" layout="${callsite}" />
  <parameter name="@callSiteLine" layout="${callsite-linenumber}" />
  <parameter name="@exception" layout="${exception:tostring}" />
  <parameter name="@eventproperties" layout="${eventproperties:tostring}" />
  <parameter name="@stacktrace" layout="${stacktrace:tostring}" />
  <parameter name="@serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
  <parameter name="@port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
  <parameter name="@url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
  <parameter name="@https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />
  <parameter name="@serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
  <parameter name="@remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />

</target>

  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="Ink3DPowderDbLogger" />
  </rules>
</nlog>

EDIT 2 I Have changed my Get Method to

public IHttpActionResult Get(string version)
    {
        IKernel kernel = new StandardKernel();
        //var resolver = new NinjectDependencyResolver(kernel);
        //var logger = (ILogger) resolver.GetService(typeof(ILogger));
        var logger = kernel.Get<ILogger>();

        try
        {
            throw new Exception("Manually thrown exception");
        }
        catch (Exception ex)
        {
            //_logger.Debug(ex);
            logger.Debug(ex);
        }

        return Ok();
    }

This gives me an error

No matching bindings are available, and the type is not self-bindable.

Which sounds like nothing is getting binded to ILogger.

Edit 3

I was looking back over my config file and noticed that I had told NLog to fail silently. I turned this off and now I am getting proper errors. There seems to be an issue with my

I get the errors

Exception: Error when setting property 'Layout' on NLog.Targets.DatabaseParameterInfo

Inner Exception: LayoutRenderer cannot be found: 'appsetting'

I'm not too sure why this can't be found I'm using the same syntax that is in the documentation for appsetting

Community
  • 1
  • 1
Jeff Finn
  • 1,975
  • 6
  • 23
  • 52

2 Answers2

1

Make sure that the controller has a parameterless public constructor.

The reason you get this error is because:

  1. you didn't configure your controller explicitly in Ninject
  2. There is an error in your configuration

Because there is no explicit registration for the controller, Ninject will try to create it for you, but since there is problem in the configuration it will return null (which is dictated by the IDependencyResolver contract).

Once you register all your controllers explicitly (you should always register all your root types explicitly in the container), Ninject will throw an expressive exception explaining what the configuration error is. Registering all root types explicitly also allows you to test your DI configuration.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hi I'm not sure I follow you, aren't parameter-less constructors created by default in .Net ? I've also been using Ninject in other projects and I've never had to explicitly configure controllers, but I will look into it. I added the config file to the original question. Thanks – Jeff Finn Oct 28 '16 at 15:53
  • 1
    @JeffFinn please register your `ErrorCheckController` using `Bind().AsSelf()`. We'll continue this discussion after you tried this :) – Steven Oct 28 '16 at 17:02
  • Thanks @Steven I have bound the controller and retried my previous steps. I am still getting the same errors – Jeff Finn Nov 01 '16 at 11:53
  • @JeffFinn: Do you have any controller with a non-default constructor that gets resolved properly? You might have forgotten to register your `NinjectDependencyResolver`. Please note that Web API requires a different dependency resolver than MVC. – Steven Nov 01 '16 at 11:57
  • the Dependency Resolver is Registered in my NinjectWebCommon. GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel); I also have other controllers that are using DI with Ninject without any problems – Jeff Finn Nov 01 '16 at 13:18
  • The dependency resolver is from Ninject.Web.WebApi.3.2.4.0 – Jeff Finn Nov 01 '16 at 13:28
  • 1
    @JeffFinn If you resolve the controller directly from the kernel using `Get()`, Ninject will throw a clear exception explaining the configuration error you made. – Steven Nov 01 '16 at 14:28
  • I have made some changes and updated the question under Edit 2. I simplified the Controller and created the Kernel directly in the API action. The error returned sounds like I'm not binding correctly to ILogger Thanks for your time. – Jeff Finn Nov 02 '16 at 14:01
  • I have debugged my configuration, there were errors, but I can now write to my DB. I still have the same issue were, it is looking for a parameter-less controller when I try to Inject ILogger into ErrorCheckController – Jeff Finn Nov 03 '16 at 14:01
1

The problem ended up being that I was trying to Inject

NLog.ILogger

instead of

Ninject.Extensions.Logging.ILogger

It is working now.

Jeff Finn
  • 1,975
  • 6
  • 23
  • 52