NLog – 将NULL写入可选数据库列

我正在使用NLog登录ASP.Net应用程序并使用Microsoft Sql Server的数据库目标。

我有一些可选的记录参数,并不总是指定。 我希望这些在没有提供时写为null,但NLog似乎总是把它们写成空字符串。

有没有办法将其配置为将null写为默认值?

参考: https : //github.com/nlog/NLog/wiki/Database-target

[编辑]

也许比我下面提出的更明显的解决方案是从使用INSERT语句转换为使用数据库过程来记录。 如果您使用数据库过程,那么您可以处理从空字符串调用到自己的null。 我不确定您是否可以将数据库过程与NLog的数据库目标一起使用。 Log4net支持它,所以我的猜测是NLog也可以。

这是一个例子(在链接问题的答案中)我找到了使用NLog使用存储过程登录数据库的人的配置。

http://nlog-forum.1685105.n2.nabble.com/Using-a-stored-procedure-for-the-DB-Target-td2621725.html

我在这里看到:

http://nlog.codeplex.com/workitem/5418

抱怨它不起作用(至少在NLog 2.0测试版中)。

两个示例之间的一个区别是工作示例使用“exec LoggingProcedureName …”而非工作示例使用“LoggingProcedureName …”

希望这可以帮助。

[编辑结束]

我无法评论为什么NLog写入emptry字符串而不是null或如何使NLog写入null而不是空字符串,但我想知道你是否可以通过其他配置以他们想要的方式工作?

日志参数何时可选? 您的代码中是否存在某些位置,您始终记录某些值以及其他未记录某些值的位置? 您(作为开发人员)能否知道哪些可选参数适用于您的应用程序的哪些部分?

您可以配置多个数据库目标,每个目标都指定了“正确”参数吗? 然后,您可以将Loggers指向适合代码位置的特定数据库目标。

假设您的应用程序(按命名空间)划分为(通常)在“之前”,“期间”和“之后”执行的代码。

在“之前”代码中,您可能正在记录参数A.在“期间”代码中,您可能正在记录参数B.在“之后”代码中,您可能正在记录参数C.因此,您的日志记录表可能包含列喜欢:

DateTime, Logger, LogLevel, A, B, C, Message, Exception 

现在,您有一个数据库目标,可以为每个日志记录语句插入所有这些值。

如果您有三个数据库目标插入如下所示的值,该怎么办:

 DataTime, Logger, LogLevel, A, Message, Exception DataTime, Logger, LogLevel, B, Message, Exception DataTime, Logger, LogLevel, C, Message, Exception 

您可以将您的部分配置为:

       

显然,这个想法可能有几个问题:

  1. 可能(或很容易)将记录器分开以匹配参数的“可选性”。

  2. 可选参数的组合可能太多以使其可行(可能与1相同)。

  3. 一次激活数据库目标日志可能不是一个好主意。 也许这会导致性能问题。

嗯,这就是我的全部。 我不知道我的想法是否会奏效,更不用说如果它是实用的话。

可能更好的解决方案是NLog允许在每个数据库参数上允许你说“发送空而不是空字符串”的额外属性。

我想我应该建议您也可以在NLog论坛中提出这个问题。 该论坛上的“Craig”今天早些时候提出了同样(或类似)的问题。 也许你是克雷格。

这是一个老问题,但由于给出的解决方案有点’ hacky ‘我想给自己的,我认为它比db程序更简单,使用案例更优雅。

您可以尝试使用比较2个表达式的NULLIF函数编写NULL,如果它们相等则返回NULL,否则返回第一个表达式( msdn NULLIF页面 )。

这样,NLog配置文件中的commandText看起来像:

 INSERT INTO [dbo].[log] ([message], [optional]) VALUES (@message, NULLIF(@optional, '')) 

NLog使用StringBuilder来创建参数值。 即使未指定参数,它也会将值初始化为builder.ToString(),这是一个空字符串。

您可以像这样更改commandText:

 INSERT INTO [dbo].[log] ([message], [optional]) VALUES ( @message, case when len(@optional) = 0 then null else @optional end ) 

这对我来说似乎是个黑客。 我希望有更好的解决方案。

我使用略有不同的方法。


因为我不喜欢编写查询,所以我创建了一个扩展,为我执行此操作,我的NLog配置如下所示:

        

请注意两件事:

  • commandText="[dbo].[Log]"必须遵循格式[Schema].[Table]
  • @Exception:null ,其中null表示它可以为空

没有元素,而是我使用此扩展从参数自动创建INSERT

 public static class NLogExtensions { // The commandText attribute must conatain at least the table name. // Each identifier must be enclosed in square brackets: [schemaName].[tableName]. public static void GenerateDatabaseTargetInsertQueries(this NLog.Config.LoggingConfiguration config) { var tableNameMatcher = new Regex(@"^(\[(?.+?)\].)?\[(?.+?)\]$"); var autoCommandTextDatabaseTargets = config.AllTargets .OfType() .Where(x => tableNameMatcher.IsMatch(x.CommandText())) .Select(x => x); foreach (var databaseTarget in autoCommandTextDatabaseTargets) { databaseTarget.CommandText = databaseTarget.CreateCommandText(); } } internal static string CommandText(this DatabaseTarget databaseTarget) { return ((NLog.Layouts.SimpleLayout)databaseTarget.CommandText).OriginalText; } internal static string CreateCommandText(this DatabaseTarget databaseTarget) { const string insertQueryTemplate = "INSERT INTO {0}({1}) VALUES({2})"; return string.Format( insertQueryTemplate, databaseTarget.CommandText(), string.Join(", ", databaseTarget.Parameters.Select(x => x.Name())), string.Join(", ", databaseTarget.Parameters.Select(x => { var sql = x.Nullable() ? string.Format("NULLIF({0}, '')", x.FullName()) : x.FullName(); // Rename the SqlParameter because otherwise SqlCommand will complain about it. x.Name = x.FullName(); return sql; }))); } } 

 public static class NLogDatabaseTarget { public static void GenerateInsertQueries() { NLog.LogManager.Configuration.GenerateDatabaseTargetInsertQueries(); } } 

另外,它可以解析参数名称并为可空参数插入NULLIF

另一个扩展可以帮助我解析它:

 public static class DatabaseParameterInfoExtensions { // https://regex101.com/r/wgoA3q/2 private static readonly Regex ParamRegex = new Regex("^(?.)(?[a-z0-9_\-]+)(?:[:](?null))?", RegexOptions.IgnoreCase); public static string Prefix(this DatabaseParameterInfo parameter) { return ParamRegex.Match(parameter.Name).Groups["prefix"].Value; } public static string Name(this DatabaseParameterInfo parameter) { return ParamRegex.Match(parameter.Name).Groups["name"].Value; } public static string FullName(this DatabaseParameterInfo parameter) { return string.Format("{0}{1}", parameter.Prefix(), parameter.Name()); } public static bool Nullable(this DatabaseParameterInfo parameter) { return ParamRegex.Match(parameter.Name).Groups["null"].Success; } } 

这意味着您需要将commandText="[dbo].[Log]"属性添加到数据库目标,删除查询并将:null添加到可为空的列的参数名称中。

在代码中你只需要调用它,扩展就会发挥作用。

 NLogDatabaseTarget.GenerateInsertQueries();