diff --git a/src/FluentCommand.Generators/Properties/launchSettings.json b/src/FluentCommand.Generators/Properties/launchSettings.json index 5fc31d63..4365fc4d 100644 --- a/src/FluentCommand.Generators/Properties/launchSettings.json +++ b/src/FluentCommand.Generators/Properties/launchSettings.json @@ -3,6 +3,10 @@ "FluentCommand.Tests": { "commandName": "DebugRoslynComponent", "targetProject": "..\\..\\test\\FluentCommand.Tests\\FluentCommand.Tests.csproj" + }, + "FluentCommand.Entities": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\test\\FluentCommand.Entities\\FluentCommand.Entities.csproj" } } } diff --git a/src/FluentCommand/DataQueryLogger.cs b/src/FluentCommand/DataQueryLogger.cs index 1195ba81..cef737be 100644 --- a/src/FluentCommand/DataQueryLogger.cs +++ b/src/FluentCommand/DataQueryLogger.cs @@ -1,29 +1,31 @@ using System.Data; +using System.Text; + +using FluentCommand.Extensions; +using FluentCommand.Internal; using Microsoft.Extensions.Logging; namespace FluentCommand; /// -/// A class for logging queries +/// A class for logging queries /// /// public partial class DataQueryLogger : IDataQueryLogger { - private readonly ILogger _logger; - private readonly IDataQueryFormatter _formatter; /// /// Initializes a new instance of the class. /// /// The logger. - /// The formatter for the data command - public DataQueryLogger(ILogger logger, IDataQueryFormatter formatter) + public DataQueryLogger(ILogger logger) { - _logger = logger; - _formatter = formatter; + Logger = logger; } + protected ILogger Logger { get; } + /// /// Log the current specified /// @@ -34,23 +36,69 @@ public DataQueryLogger(ILogger logger, IDataQueryFormatter form /// command public virtual void LogCommand(IDbCommand command, TimeSpan duration, Exception exception = null, object state = null) { - if (_logger == null) + if (Logger == null) return; if (command is null) throw new ArgumentNullException(nameof(command)); - var output = _formatter.FormatCommand(command, duration, exception); + var elapsed = duration.TotalMilliseconds; + var commandType = command.CommandType; + var commandTimeout = command.CommandTimeout; + var commandText = command.CommandText; + var parameterText = FormatParameters(command); if (exception == null) - LogCommand(output); + LogCommand(Logger, elapsed, commandType, commandTimeout, commandText, parameterText); else - LogError(output, exception); + LogError(Logger, elapsed, commandType, commandTimeout, commandText, parameterText, exception); + } + + protected static string FormatParameters(IDbCommand command) + { + if (command is null || command.Parameters == null || command.Parameters.Count == 0) + return string.Empty; + + var parameterText = StringBuilderCache.Acquire(); + + foreach (IDataParameter parameter in command.Parameters) + { + int precision = 0; + int scale = 0; + int size = 0; + + if (parameter is IDbDataParameter dataParameter) + { + precision = dataParameter.Precision; + scale = dataParameter.Scale; + size = dataParameter.Size; + } + + parameterText + .AppendLineIf(() => parameterText.Length > 0) + .Append("-- ") + .Append(parameter.ParameterName) + .Append(": ") + .Append(parameter.Direction) + .Append(" ") + .Append(parameter.DbType) + .Append("(Size=") + .Append(size) + .Append("; Precision=") + .Append(precision) + .Append("; Scale=") + .Append(scale) + .Append(") [") + .Append(parameter.Value) + .Append("]"); + } + + return parameterText.ToString(); } - [LoggerMessage(0, LogLevel.Debug, "{output}")] - private partial void LogCommand(string output); + [LoggerMessage(0, LogLevel.Debug, "Executed DbCommand ({Elapsed} ms) [CommandType='{CommandType}', CommandTimeout='{CommandTimeout}']\r\n{CommandText}\r\n{ParameterText}")] + protected static partial void LogCommand(ILogger logger, double elapsed, CommandType commandType, int commandTimeout, string commandText, string parameterText); - [LoggerMessage(1, LogLevel.Error, "{output}")] - private partial void LogError(string output, Exception exception); + [LoggerMessage(1, LogLevel.Error, "Error Executing DbCommand ({Elapsed} ms) [CommandType='{CommandType}', CommandTimeout='{CommandTimeout}']\r\n{CommandText}\r\n{ParameterText}")] + protected static partial void LogError(ILogger logger, double elapsed, CommandType commandType, int commandTimeout, string commandText, string parameterText, Exception exception); } diff --git a/src/FluentCommand/Extensions/StringBuilderExtensions.cs b/src/FluentCommand/Extensions/StringBuilderExtensions.cs new file mode 100644 index 00000000..45b485dd --- /dev/null +++ b/src/FluentCommand/Extensions/StringBuilderExtensions.cs @@ -0,0 +1,113 @@ +using System.Text; + +namespace FluentCommand.Extensions; + +/// +/// extension methods +/// +public static class StringBuilderExtensions +{ + /// + /// Appends a copy of the specified string followed by the default line terminator to the end of the StringBuilder object. + /// + /// The StringBuilder instance to append to. + /// A composite format string. + /// An object array that contains zero or more objects to format. + public static StringBuilder AppendLine(this StringBuilder sb, string format, params object[] args) + { + sb.AppendFormat(format, args); + sb.AppendLine(); + return sb; + } + + /// + /// Appends a copy of the specified string if is met. + /// + /// The StringBuilder instance to append to. + /// The string to append. + /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. + public static StringBuilder AppendIf(this StringBuilder sb, string text, Func condition = null) + { + var c = condition ?? (s => !string.IsNullOrEmpty(s)); + + if (c(text)) + sb.Append(text); + + return sb; + } + + /// + /// Appends a copy of the specified string if is met. + /// + /// The StringBuilder instance to append to. + /// The string to append. + /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. + public static StringBuilder AppendIf(this StringBuilder sb, string text, bool condition) + { + if (condition) + sb.Append(text); + + return sb; + } + + /// + /// Appends a copy of the specified string followed by the default line terminator if is met. + /// + /// The StringBuilder instance to append to. + /// The string to append. + /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. + public static StringBuilder AppendLineIf(this StringBuilder sb, string text, Func condition = null) + { + var c = condition ?? (s => !string.IsNullOrEmpty(s)); + + if (c(text)) + sb.AppendLine(text); + + return sb; + } + + /// + /// Appends a copy of the specified string followed by the default line terminator if is met. + /// + /// The StringBuilder instance to append to. + /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. + public static StringBuilder AppendLineIf(this StringBuilder sb, Func condition) + { + if (condition()) + sb.AppendLine(); + + return sb; + } + + /// + /// Concatenates and appends the members of a collection, using the specified separator between each member. + /// + /// The type of the members of values. + /// A reference to this instance after the append operation has completed. + /// The string to use as a separator. separator is included in the concatenated and appended strings only if values has more than one element. + /// A collection that contains the objects to concatenate and append to the current instance of the string builder. + /// A reference to this instance after the append operation has completed. + public static StringBuilder AppendJoin(this StringBuilder sb, string separator, IEnumerable values) + { + if (sb is null) + throw new ArgumentNullException(nameof(sb)); + if (values is null) + throw new ArgumentNullException(nameof(values)); + + separator ??= string.Empty; + + var wroteValue = false; + + foreach (var value in values) + { + if (wroteValue) + sb.Append(separator); + + sb.Append(value); + wroteValue = true; + } + + return sb; + } + +} diff --git a/src/FluentCommand/Extensions/StringExtensions.cs b/src/FluentCommand/Extensions/StringExtensions.cs index 2b705298..5c4ce408 100644 --- a/src/FluentCommand/Extensions/StringExtensions.cs +++ b/src/FluentCommand/Extensions/StringExtensions.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text; +#nullable enable + namespace FluentCommand.Extensions; /// @@ -16,7 +19,7 @@ public static class StringExtensions /// /// true if is null or empty; otherwise, false. /// - public static bool IsNullOrEmpty(this string item) + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? item) { return string.IsNullOrEmpty(item); } @@ -28,7 +31,7 @@ public static bool IsNullOrEmpty(this string item) /// /// true if is null or empty; otherwise, false. /// - public static bool IsNullOrWhiteSpace(this string item) + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? item) { if (item == null) return true; @@ -47,112 +50,19 @@ public static bool IsNullOrWhiteSpace(this string item) /// /// true if the specified is not ; otherwise, false. /// - public static bool HasValue(this string value) + public static bool HasValue([NotNullWhen(true)] this string? value) { return !string.IsNullOrEmpty(value); } /// - /// Uses the string as a format + /// Replaces the format item in a specified string with the string representation of a corresponding object in a specified array. /// - /// A String reference - /// Object parameters that should be formatted - /// Formatted string - public static string FormatWith(this string format, params object[] args) + /// A composite format string + /// An object array that contains zero or more objects to format + /// A copy of format in which the format items have been replaced by the string representation of the corresponding objects in args + public static string FormatWith(this string format, params object?[] args) { - if (format == null) - throw new ArgumentNullException("format"); - return string.Format(format, args); } - - /// - /// Appends a copy of the specified string followed by the default line terminator to the end of the StringBuilder object. - /// - /// The StringBuilder instance to append to. - /// A composite format string. - /// An object array that contains zero or more objects to format. - public static StringBuilder AppendLine(this StringBuilder sb, string format, params object[] args) - { - sb.AppendFormat(format, args); - sb.AppendLine(); - return sb; - } - - /// - /// Appends a copy of the specified string if is met. - /// - /// The StringBuilder instance to append to. - /// The string to append. - /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. - public static StringBuilder AppendIf(this StringBuilder sb, string text, Func condition = null) - { - var c = condition ?? (s => !string.IsNullOrEmpty(s)); - - if (c(text)) - sb.Append(text); - - return sb; - } - - /// - /// Appends a copy of the specified string if is met. - /// - /// The StringBuilder instance to append to. - /// The string to append. - /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. - public static StringBuilder AppendIf(this StringBuilder sb, string text, bool condition) - { - if (condition) - sb.Append(text); - - return sb; - } - - /// - /// Appends a copy of the specified string followed by the default line terminator if is met. - /// - /// The StringBuilder instance to append to. - /// The string to append. - /// The condition delegate to evaluate. If condition is null, String.IsNullOrWhiteSpace method will be used. - public static StringBuilder AppendLineIf(this StringBuilder sb, string text, Func condition = null) - { - var c = condition ?? (s => !string.IsNullOrEmpty(s)); - - if (c(text)) - sb.AppendLine(text); - - return sb; - } - - /// - /// Concatenates and appends the members of a collection, using the specified separator between each member. - /// - /// The type of the members of values. - /// A reference to this instance after the append operation has completed. - /// The string to use as a separator. separator is included in the concatenated and appended strings only if values has more than one element. - /// A collection that contains the objects to concatenate and append to the current instance of the string builder. - /// A reference to this instance after the append operation has completed. - public static StringBuilder AppendJoin(this StringBuilder sb, string separator, IEnumerable values) - { - if (sb is null) - throw new ArgumentNullException(nameof(sb)); - if (values is null) - throw new ArgumentNullException(nameof(values)); - - separator ??= string.Empty; - - var wroteValue = false; - - foreach (var value in values) - { - if (wroteValue) - sb.Append(separator); - - sb.Append(value); - wroteValue = true; - } - - return sb; - } } diff --git a/src/FluentCommand/FluentCommand.csproj b/src/FluentCommand/FluentCommand.csproj index 4933a7be..b48f5483 100644 --- a/src/FluentCommand/FluentCommand.csproj +++ b/src/FluentCommand/FluentCommand.csproj @@ -7,6 +7,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/FluentCommand/Internal/IsExternalInit.cs b/src/FluentCommand/Internal/IsExternalInit.cs deleted file mode 100644 index fee341d3..00000000 --- a/src/FluentCommand/Internal/IsExternalInit.cs +++ /dev/null @@ -1,8 +0,0 @@ -#if NETSTANDARD2_0 -using System.ComponentModel; - -namespace System.Runtime.CompilerServices; - -[EditorBrowsable(EditorBrowsableState.Never)] -sealed class IsExternalInit { } -#endif