How to change NLog target's connection string at runtime?


December 2018


2.5k time


In my WebAPI application I am trying to make logging into several separate databases in Application_BeginRequest based on my custom header value.

To accomplish that I tried to modify target in Nlog.config like so:

<target name="database" 
        commandText="..sql command..">..params..</target>

and in Application_BeginRequest I tried to set that myConnectionstring like so:

GlobalDiagnosticsContext.Set("myConnectionString", connectionString);

But that doesn't work and in NLog's internalLogFile error shown up:

2015-02-19 12:12:48.8776 Error Error when writing to database System.InvalidOperationException: The ConnectionString property has not been initialized.
   at System.Data.SqlClient.SqlConnection.PermissionDemand()
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at NLog.Targets.DatabaseTarget.OpenConnection(String connectionString)
   at NLog.Targets.DatabaseTarget.WriteEventToDatabase(LogEventInfo logEvent)
   at NLog.Targets.DatabaseTarget.Write(LogEventInfo logEvent)

I tried to write into db (with constant connection string configuration) following parameters:

<parameter name="@cn" layout="${gdc:myConnectionstring}"/>
<parameter name="@cn1" layout="${gdc:item=myConnectionstring}"/>

But they appeared to be empty, however when I debug my WebApi Action that throws error that I expect to be written in database, GlobalDiagnosticsContext.Get("myConnectionString") returns expected value.

What I am doing wrong? Why I can see expected value in GlobalDiagnosticsContext.Get("myConnectionString") but ${gdc:item=myConnectionstring} resolves to empty string?

2 answers


Спасибо за решение!

Тем не менее, если вы хотите использовать асинхронные целевую базу данных, вам нужно будет получить обернутую цель первым:

DatabaseTarget databaseTarget;
var target = LogManager.Configuration.FindTargetByName("database");
if (target is AsyncTargetWrapper)
    databaseTarget = (target as AsyncTargetWrapper).WrappedTarget as DatabaseTarget;
    databaseTarget = target as DatabaseTarget;
if (databaseTarget == null)
    throw new NullReferenceException("Can't find DatabaseTarget with name 'database'");

databaseTarget.ConnectionString = connectionStringSelected.ConnectionString;

Обнаружили альтернативный способ сделать что:

var databaseTarget = (DatabaseTarget)LogManager.Configuration.FindTargetByName("database");
databaseTarget.ConnectionString = connectionString;

Однако до сих пор не ясно , почему GlobalDiagnosticsContextвещи не работают ..