0

I'm using the latest NLog, ASP.NET Core, EF Core.

I wrote a custom log target which saves via EF:

public  class MyEFTarget : TargetWithLayout
{
    private readonly IMyContext _context;

    public MyEFTarget(IMyContext context) : base()
    {
        _context = context;
    }

    protected override void Write(LogEventInfo logEvent)
    {
        // and so on...
    }

}

My Startup:

public void ConfigureServices(IServiceCollection services)
{
  // register context with DI (as scoped)
  services.AddDbContext<MyContext>(o => o.UseSqlite(config.GetConnectionString("Default")));
  services.AddScoped<IMyContext, MyContext>();      

  // target depends on context, so must also be registered with DI
  // I chose scoped so it's the same as the context
  services.AddScoped<MyEFTarget>();

  // ...and so on
}


public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    ILoggerFactory loggerFactory, IServiceProvider serviceProvider)
{
  // normal NLog config 
  env.ConfigureNLog("NLog.config");
  loggerFactory.AddNLog();
  app.AddNLogWeb();

  // register target
  Target.Register<MyEFTarget>("MyEFTarget");

  // add target + rule programmatically
  var target = serviceProvider.GetService<MyEFTarget>();
  var rule = new LoggingRule("*", LogLevel.Info, target);
  LogManager.Configuration.AddTarget("MyEFTarget", target);    // problem is here ********
  LogManager.Configuration.LoggingRules.Add(rule);
  LogManager.ReconfigExistingLoggers();

  // ...and so on
}

The problem is that NLog caches the target instance for the duration of the app...I assume? So it's basically a singleton. And it's keeping a reference to my EF context, which might be disposed.

Is there a better way?

To restate the problem generically (because it's not an EF issue), "how do I register/add a custom target which has short-lived dependencies"?

grokky
  • 8,537
  • 20
  • 62
  • 96
  • You could create a flush pattern and only open DB connection when flushing. If you can inject the IServiceProvider and `GetService()` from it when you need it. – Joel Harkes Mar 06 '17 at 15:45
  • @JoelHarkes The connection is handled by EF, I don't have control over it. – grokky Mar 06 '17 at 15:53
  • why you use AddScoped if you have services.AddDbContext? – Alexan Mar 06 '17 at 16:16
  • I've search another thing, but I must to say: 1) Registering dbContext as interface should be like this: `services.AddScoped(services => services.GetRequiredService());` 2) Target not a singleton it's might been created and destroyed multiple times throught app lifetime. At the end of app life NLog calls `Dispose()` of the base `Target` class that can be overriden. – Leonid Pavlov Jun 03 '22 at 08:14
  • It was 5 years ago in earlier versions of NLog, but somebody can read and be misinformed – Leonid Pavlov Jun 03 '22 at 08:23

1 Answers1

1

When the NLog configuration is loaded, NLog will create an instance of your target with ConfigurationItemFactory.Default.CreateInstance and the target will be only recreated when reloading the configuration.

If you need a new EF context every time, I would recommend to load the EF context in protected override void Write(LogEventInfo logEvent)

Julian
  • 33,915
  • 22
  • 119
  • 174
  • Hmm, that's a little tricky. To create a context in there, you'd need some kind of dependency. But any dependency passed by constructor injection would be cached for app's lifetime. So same problem as before. I suppose then I need to use service locator pattern instead. Yuck! :-) – grokky Mar 10 '17 at 10:08
  • You gave me an idea, which I [posted about separately](https://stackoverflow.com/questions/42716038/is-it-safe-to-cache-iserviceprovider-for-an-apps-lifetime). Once I figure it out I'll then update this question. Thanks for putting me on the right track. – grokky Mar 10 '17 at 10:26
  • This is how I'm currently doing it, but it take A LOT of effort to get it working properly. It is unfortunate that NLog doesn't allow adding the custom target by lambda/factory, so new one is created every time, and thus allow the target to have its own non-singleton dependencies. Nontheless, thatnks for putting me on the right track. – grokky Mar 20 '17 at 10:14