我已经看过这个问题,但是因为它使用了NLog.config文件,所以我无法将答案转化为我需要的内容。 NLog - Write NULL to optional database column
我刚刚开始使用NLog,因为它被建议作为我问的另一个问题的答案。当我在类库中使用它时,我不想在每个使用该库的应用程序中放置NLog.config文件,我使用的是配置API。
我想要做的是在我的日志表中将某些列设置为NULL,而不需要/指定它们。我可以让NLog写一个空字符串,但不是NULL。
我试图配置NLog来执行存储过程,但它似乎不起作用。
目前我正在设置一个包含用户名,密码等的数据库目标,以及以下命令文本:
target.CommandText = "insert into Log(time_stamp,log_level,logger,message,exception_type,target_site,stack_trace,data,inner_exception) values(@time_stamp, @level, @logger, @message, @type, @target, @trace, @data, @inner);";
然后向目标添加参数,例如
var param = new DatabaseParameterInfo();
param.Name = "@time_stamp";
param.Layout = "${date}";
target.Parameters.Add(param);
所以这只能添加一个空字符串:
var param = new DatabaseParameterInfo();
param.Name = "@type";
param.Layout = "";
target.Parameters.Add(param);
但是这不起作用,因为需要布局:
var param = new DatabaseParameterInfo();
param.Name = "@time_stamp";
param.Layout = null;
target.Parameters.Add(param);
我尝试过执行存储过程,按如下方式更改命令文本(之后添加如上所示的参数,包括空字符串而不是空字符串):
target.CommandText = "exec usp_InsertLog @time_stamp, @level, @logger, @message, @type, @target, @trace, @data, @inner";
根据SQL Server Profiler(为清晰起见,换行符)生成此内容:
exec sp_executesql N'
exec usp_InsertLog @time_stamp, @level, @logger, @message, @type, @target, @trace, @data, @inner',
N'@time_stamp nvarchar(19),@level nvarchar(5),@logger nvarchar(62),@message nvarchar(8),@type nvarchar(4000),@target nvarchar(4000),@trace nvarchar(4000),@data nvarchar(4000),@inner nvarchar(4000)',
@time_stamp=N'01/03/2014 17:00:38',@level=N'Debug',@logger=N'Testbed, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',@message=N'an error',@type=N'',@target=N'',@trace=N'',@data=N'',@inner=N''
如果我可以让存储过程工作,那么我可以让它插入NULL而不是空字符串,如果那是NLog传入的。
此处是存储过程,无论值是否为空,它都不起作用:
ALTER PROCEDURE [dbo].[usp_InsertLog]
@time_stamp datetime,
@level nvarchar(100),
@logger nvarchar(100),
@message nvarchar(MAX),
@type nvarchar(150) = NULL,
@target nvarchar(100) = NULL,
@trace nvarchar(MAX) = NULL,
@data nvarchar(MAX) = NULL,
@inner nvarchar(MAX) = NULL
AS
SET NOCOUNT ON
BEGIN TRY
insert into Log(
time_stamp,
log_level,
logger,
[message],
exception_type,
target_site,
stack_trace,
data,
inner_exception)
values(
@time_stamp,
@level,
@logger,
@message,
@type,
@target,
@trace,
@data,
@inner)
END TRY
我有什么遗漏,或者更好的方法吗?
非常感谢!
答案 0 :(得分:5)
这是我在NLog - Write NULL to optional database column上发布的一个解决方案,我希望它有用,因为我认为存储过程解决方案就像用大锤敲碎螺母一样。
您可以尝试使用比较2个表达式的NULLIF
函数编写NULL,如果它们相等则返回NULL,否则返回第一个表达式(msdn NULLIF page)。
这样,NLog配置文件中的commandText将如下所示:
INSERT INTO [dbo].[log] ([message], [optional])
VALUES (@message, NULLIF(@optional, ''))
答案 1 :(得分:1)
我使用的方法略有不同。
因为我不喜欢编写查询,所以我创建了一个扩展程序,为我执行此操作,我的NLog配置如下所示:
<target xsi:type="Database" name="Log" commandText="[dbo].[Log]" dbProvider="System.Data.SqlClient" connectionString="..">
<parameter name="@Timestamp" layout="${longdate:universalTime=true}" />
<parameter name="@LogLevel" layout="${level:uppercase=true}" />
<parameter name="@Logger" layout="${logger}" />
<parameter name="@Message" layout="${message}" />
<parameter name="@Exception:null" layout="${onexception:${exceptionLayout}}" />
</target>
请注意两件事:
commandText="[dbo].[Log]"
[Schema].[Table]
@Exception:null
null
表示可以为空的<{li} 没有<commandText>
元素,而是使用此扩展程序自动从参数创建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(@"^(\[(?<schemaName>.+?)\].)?\[(?<tableName>.+?)\]$");
var autoCommandTextDatabaseTargets =
config.AllTargets
.OfType<DatabaseTarget>()
.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("^(?<prefix>.)(?<name>[a-z0-9_\-]+)(?:[:](?<null>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();
答案 2 :(得分:0)
在sql server中你可以做这样的事情
DECLARE @Var1 INT, @Var2 INT, @Var3 INT, @Var4 VARCHAR(10)
, @Var5 VARCHAR(5), @SubsituteVal_Int INT , @SubsituteVal_Char VARCHAR(10)
SET @Var1 = 1;
SET @Var3 = 3; --<-- Didnt Assign any Values to @Var2 and @Var4
SET @Var5 = 'Fifth';
SET @SubsituteVal_Int = 100 --<-- Provided Substitute values if actual
SET @SubsituteVal_Char = 'No Value'; -- values are null.
/ 使用COALESCE函数提供实际值和替换值 /
DECLARE @t TABLE(Column1 INT, Column2 INT, Column3 INT, Column4 VARCHAR(10), Column5 VARCHAR(10))
INSERT INTO @t
VALUES (COALESCE(@Var1, @SubsituteVal_Int),COALESCE(@Var2, @SubsituteVal_Int)
,COALESCE(@Var3, @SubsituteVal_Int),COALESCE(@Var4, @SubsituteVal_Char),COALESCE(@Var5, @SubsituteVal_Char))
SELECT * FROM @t
结果集
╔═════════╦═════════╦═════════╦══════════╦═════════╗
║ Column1 ║ Column2 ║ Column3 ║ Column4 ║ Column5 ║
╠═════════╬═════════╬═════════╬══════════╬═════════╣
║ 1 ║ 100 ║ 3 ║ No Value ║ Fifth ║
╚═════════╩═════════╩═════════╩══════════╩═════════╝
您的过程
对于您的程序,您可以执行类似
的操作BEGIN TRY
insert into Log(
time_stamp,
log_level,
logger,
[message],
exception_type,
target_site,
stack_trace,
data,
inner_exception)
values(
COALESCE(@time_stamp, @Subsitute_Val1)
,COALESCE(@level, @Subsitute_Val2)
,COALESCE(@logger, @Subsitute_Val3)
.
.. so on.... )
END TRY
答案 3 :(得分:0)
NLog版本。 4.7.4添加了参数选项AllowDbNull
,因此您可以这样做:
var param = new DatabaseParameterInfo();
param.Name = "@type";
param.Layout = "";
param.AllowDbNull = true;
target.Parameters.Add(param);
在allowDbNull="true"
时,Nlog会自动将空字符串/无输出转换为DbNull值。