diff --git a/CHANGES.md b/CHANGES.md
index 855f0ecb..8ccbf155 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,8 @@
+# 9.0.0
+* Updated SqlClient to 6.1.1 and removed Azure.Identity workaround for issue #624
+* Updated all dependencies except .NET 9 NuGets
+* Implemented #636: Added support for non-clustered index sort direction (thanks to @gumbarros)
+
# 8.2.2
* Fixed issue #624: Enforce new version of transient dependency to fix vulnerability and avoid nuget.org version de-listing until SqlClient 6.1 is released.
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 2d1146b8..8bfd79ae 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -6,7 +6,7 @@ Whenever the `dev` branch is updated (after merging a pull request), the `Releas
## Creating a latest Release
-### Normal Update (no major version change) {#normal-update}
+### Normal Update (no major version change)
1. On the `dev` branch, update CHANGES.md and `VersionPrefix` in Serilog.Sinks.MSSqlServer.csproj.
@@ -18,6 +18,6 @@ Whenever the `dev` branch is updated (after merging a pull request), the `Releas
1. On the `dev` branch, update CHANGES.md and increase the major version in `VersionPrefix` in Serilog.Sinks.MSSqlServer.csproj. Also set `EnablePackageValidation` to false because on an intial release of a new major version you don't have a baseline version yet on nuget.org to compare with.
-1. Create a PR to merge the `dev` branch into `main`. The `Release` action will be triggered. This works the same as described above under [Normal Update]({#normal-update).
+1. Create a PR to merge the `dev` branch into `main`. The `Release` action will be triggered. This works the same as described above under [Normal Update](#normal-update-no-major-version-change).
1. After the release is done make some changes in Serilog.Sinks.MSSqlServer.csproj on the `dev` branch. Set `EnablePackageValidation` back to true and `PackageValidationBaselineVersion` to the version of the new major release you just created (e.g. 7.0.0). Then also increase the patch version number in `VersionPrefix` (e.g. 7.0.1).
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f8506085..1b69a1af 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,23 +3,22 @@
true
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
-
+
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index e9a7091a..3c0c7934 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,7 @@ columnOpts.Store.Add(StandardColumn.LogEvent);
columnOpts.LogEvent.DataLength = 2048;
columnOpts.PrimaryKey = columnOpts.TimeStamp;
columnOpts.TimeStamp.NonClusteredIndex = true;
+columnOpts.TimeStamp.NonClusteredIndexDirection = SqlIndexDirection.Desc;
var log = new LoggerConfiguration()
.WriteTo.MSSqlServer(
@@ -349,6 +350,7 @@ Each Standard Column in the `ColumnOptions.Store` list and any custom columns yo
* `AllowNull`
* `DataLength`
* `NonClusteredIndex`
+* `NonClusteredIndexDirection`
### ColumnName
@@ -412,9 +414,20 @@ Supported SQL column data types that use this property:
Any individual column can be defined as a non-clustered index, including the table primary key. Use this with caution, indexing carries a relatively high write-throughput penalty. One way to mitigate this is to keep non-clustered indexes offline and use batch reindexing on a scheduled basis.
+### NonClusteredIndexDirection
+
+Specifies the sort direction (`ASC` or `DESC`) for a non-clustered index on the SQL column.
+The default value is `ASC`.
+
+It is especially useful for the timestamp column,
+where choosing the correct sort direction can optimize query performance for workloads that typically scan
+recent data first.
+
+This only has effect if `NonClusteredIndex` is `true`.
+
## Standard Columns
-By default (and consistent with the SQL DDL to create a table shown earlier) these columns are included in a new `ColumnOptions.Store` list:
+By default (and consistent with the SQL DDL to create a table shown earlier), these columns are included in a new `ColumnOptions.Store` list:
- `StandardColumn.Id`
- `StandardColumn.Message`
diff --git a/sample/WorkerServiceDemo/appsettings.json b/sample/WorkerServiceDemo/appsettings.json
index 1ca39a65..72d4cc40 100644
--- a/sample/WorkerServiceDemo/appsettings.json
+++ b/sample/WorkerServiceDemo/appsettings.json
@@ -25,7 +25,10 @@
"removeStandardColumns": [ "MessageTemplate", "Properties" ],
"timeStamp": {
"columnName": "Timestamp",
- "convertToUtc": false
+ "allowNulls": false,
+ "convertToUtc": false,
+ "nonClusteredIndex": true,
+ "nonClusteredIndexDirection": "Desc"
},
"customColumns": [
{
@@ -43,6 +46,12 @@
"propertyName": "NonstructuredProperty.WithNameContainingDots.Name",
"resolveHierarchicalPropertyName": false,
"dataType": "12"
+ },
+ {
+ "columnName": "AdditionalColumn4",
+ "dataType": "0",
+ "nonClusteredIndex": true,
+ "nonClusteredIndexDirection": "Desc"
}
]
},
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs
index ce585d3d..dd59c704 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs
@@ -34,6 +34,8 @@ private static void SetCommonColumnOptions(IConfigurationSection section, SqlCol
SetProperty.IfNotNull(section["allowNull"], (val) => target.AllowNull = val);
SetProperty.IfNotNull(section["dataLength"], (val) => target.DataLength = val);
SetProperty.IfNotNull(section["nonClusteredIndex"], (val) => target.NonClusteredIndex = val);
+ SetProperty.IfEnumNotNull(section["nonClusteredIndexDirection"],
+ (val) => target.NonClusteredIndexDirection = val);
}
private static void AddRemoveStandardColumns(IConfigurationSection config, ColumnOptions columnOptions)
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs
index 1d669c4d..fbb487b8 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs
@@ -29,7 +29,25 @@ public static void IfNotNull(string value, PropertySetter setter)
var setting = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
setter(setting);
}
- // don't change the property if the conversion fails
+ // don't change the property if the conversion fails
+ catch (InvalidCastException) { }
+ catch (OverflowException) { }
+ }
+
+ ///
+ /// This will only set a value (execute the PropertySetter delegate) if the value is non-null.
+ /// It also converts the provided value to the requested enum type. This allows configuration to only
+ /// apply property changes when external configuration has actually provided a value.
+ ///
+ public static void IfEnumNotNull(string value, PropertySetter setter) where T : System.Enum
+ {
+ if (value == null || setter == null) return;
+ try
+ {
+ var setting = (T)Enum.Parse(typeof(T), value, ignoreCase: true);
+ setter(setting);
+ }
+ // don't change the property if the conversion fails
catch (InvalidCastException) { }
catch (OverflowException) { }
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs
index cd00467c..5b3cebbd 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs
@@ -83,6 +83,13 @@ public string NonClusteredIndex
set { this[nameof(NonClusteredIndex)] = value; }
}
+ [ConfigurationProperty("NonClusteredIndexDirection")]
+ public string NonClusteredIndexDirection
+ {
+ get { return (string)this[nameof(NonClusteredIndexDirection)]; }
+ set { this[nameof(NonClusteredIndexDirection)] = value; }
+ }
+
internal SqlColumn AsSqlColumn()
{
var sqlColumn = new SqlColumn();
@@ -106,6 +113,9 @@ internal SqlColumn AsSqlColumn()
SetProperty.IfProvided(this, nameof(NonClusteredIndex), (val) => sqlColumn.NonClusteredIndex = val);
+ SetProperty.IfEnumProvided(this, nameof(NonClusteredIndexDirection),
+ (val) => sqlColumn.NonClusteredIndexDirection = val);
+
return sqlColumn;
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs
index f29861ae..15173028 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs
@@ -22,6 +22,22 @@ public static void IfProvided(ConfigurationElement element, string propertyNa
IfNotNull((string)property.Value, setter);
}
+ ///
+ /// Test the underlying enum property collection's value-origin flag for a non-default string value. Empty strings allowed.
+ ///
+ public static void IfEnumProvided(ConfigurationElement element, string propertyName, PropertySetter setter)
+ where T : System.Enum
+ {
+ if (element == null)
+ {
+ return;
+ }
+
+ var property = element.ElementInformation.Properties[propertyName];
+ if (property.ValueOrigin == PropertyValueOrigin.Default) return;
+ IfEnumNotNull((string)property.Value, setter);
+ }
+
///
/// Test the underlying property collection's value-origin flag for a non-default, non-null, non-empty string value.
///
diff --git a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs
index 21d62d3c..42e675a7 100644
--- a/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs
@@ -26,6 +26,7 @@ private static void SetCommonColumnOptions(ColumnConfig source, SqlColumn target
SetProperty.IfProvided(source, nameof(target.AllowNull), value => target.AllowNull = value);
SetProperty.IfProvided(source, nameof(target.DataLength), value => target.DataLength = value);
SetProperty.IfProvided(source, nameof(target.NonClusteredIndex), value => target.NonClusteredIndex = value);
+ SetProperty.IfEnumProvided(source, nameof(target.NonClusteredIndexDirection), value => target.NonClusteredIndexDirection = value);
}
private static void ReadPropertiesColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions)
diff --git a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
index 4f1da52c..5e6fb2f7 100644
--- a/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
+++ b/src/Serilog.Sinks.MSSqlServer/Serilog.Sinks.MSSqlServer.csproj
@@ -2,8 +2,8 @@
A Serilog sink that writes events to Microsoft SQL Server and Azure SQL
- 8.2.2
- true
+ 9.0.0
+ false
8.0.0
Michiel van Oudheusden;Christian Kadluba;Serilog Contributors
netstandard2.0;net462;net472;net8.0
@@ -36,7 +36,6 @@
-
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateTableWriter.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateTableWriter.cs
index e1ce1026..d0d3bcb6 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateTableWriter.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateTableWriter.cs
@@ -54,7 +54,7 @@ public string GetSql()
// collect non-PK indexes for separate output after the table DDL
if (common != null && common.NonClusteredIndex && common != _columnOptions.PrimaryKey)
- ix.AppendLine(Invariant($"CREATE NONCLUSTERED INDEX [IX{indexCount++}_{_tableName}] ON [{_schemaName}].[{_tableName}] ([{common.ColumnName}]);"));
+ ix.AppendLine(Invariant($"CREATE NONCLUSTERED INDEX [IX{indexCount++}_{_tableName}] ON [{_schemaName}].[{_tableName}] ([{common.ColumnName}] {common.NonClusteredIndexDirection});"));
}
}
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlColumn.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlColumn.cs
index 626e1961..340d20ca 100644
--- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlColumn.cs
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlColumn.cs
@@ -100,6 +100,12 @@ public SqlDbType DataType
///
public bool NonClusteredIndex { get; set; }
+ ///
+ /// Specifies the sort direction for a non-clustered index on the SQL column.
+ /// The default value is . This property is only used when auto-creating a log table.
+ ///
+ public SqlIndexDirection NonClusteredIndexDirection { get; set; }
+
///
/// The name of the Serilog property to use as the value when filling the DataTable.
/// If not specified, the ColumnName and PropertyName are the same.
diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlIndexDirection.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlIndexDirection.cs
new file mode 100644
index 00000000..247f154e
--- /dev/null
+++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlIndexDirection.cs
@@ -0,0 +1,18 @@
+namespace Serilog.Sinks.MSSqlServer
+{
+ ///
+ /// Defines the sort order for an SQL index.
+ ///
+ public enum SqlIndexDirection
+ {
+ ///
+ /// Represents the ascending direction for SQL indexing.
+ ///
+ Asc,
+
+ ///
+ /// Represents the descending direction for SQL indexing.
+ ///
+ Desc
+ }
+}
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProviderTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProviderTests.cs
index 8c571c27..35d38161 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProviderTests.cs
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProviderTests.cs
@@ -105,15 +105,16 @@ public void ConfigureColumnOptionsAddsColumnIdWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = false;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("id", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("id", columnName, dataType, allowNull, nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Id);
+ AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, nonClusteredIndexDirection, result.Id);
}
[Fact]
@@ -157,15 +158,18 @@ public void ConfigureColumnOptionsAddsColumnLevelWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("level", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("level", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Level);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.Level);
}
[Fact]
@@ -192,15 +196,18 @@ public void ConfigureColumnOptionsAddsColumnPropertiesWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("properties", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("properties", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Properties);
+ AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex,
+ nonClusteredIndexDirection, result.Properties);
}
[Fact]
@@ -410,15 +417,18 @@ public void ConfigureColumnOptionsAddsColumnTimeStampWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Desc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("timeStamp", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("timeStamp", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.TimeStamp);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.TimeStamp);
}
[Fact]
@@ -445,15 +455,18 @@ public void ConfigureColumnOptionsAddsColumnLogEventWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("logEvent", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("logEvent", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.LogEvent);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.LogEvent);
}
[Fact]
@@ -496,15 +509,18 @@ public void ConfigureColumnOptionsAddsColumnTraceIdWithSpecifiedOptions()
var dataType = SqlDbType.NVarChar;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("traceId", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("traceId", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.TraceId);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.TraceId);
}
[Fact]
@@ -515,15 +531,18 @@ public void ConfigureColumnOptionsAddsColumnSpanIdWithSpecifiedOptions()
var dataType = SqlDbType.NVarChar;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Desc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("spanId", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("spanId", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.SpanId);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.SpanId);
}
[Fact]
@@ -534,15 +553,18 @@ public void ConfigureColumnOptionsAddsColumnMessageWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("message", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("message", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Message);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.Message);
}
[Fact]
@@ -553,15 +575,18 @@ public void ConfigureColumnOptionsAddsColumnExceptionWithSpecifiedOptions()
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("exception", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("exception", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Exception);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.Exception);
}
[Fact]
@@ -572,15 +597,18 @@ public void ConfigureColumnOptionsAddsColumnMessageTemplateWithSpecifiedOptions(
var dataType = SqlDbType.Bit;
var allowNull = true;
var nonClusteredIndex = true;
+ var nonClusteredIndexDirection = SqlIndexDirection.Asc;
SetupConfigurationSectionMocks();
- SetupColumnSectionMock("messageTemplate", columnName, dataType, allowNull, nonClusteredIndex);
+ SetupColumnSectionMock("messageTemplate", columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection);
var sut = new MicrosoftExtensionsColumnOptionsProvider();
// Act
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
// Assert
- AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.MessageTemplate);
+ AssertColumnSqlOptions(columnName, dataType, allowNull,
+ nonClusteredIndex, nonClusteredIndexDirection, result.MessageTemplate);
}
[Fact]
@@ -697,12 +725,19 @@ public void ConfigureColumnOptionsThrowsWhenSettingPrimaryKeyColumnNameToUndefin
Assert.Throws(() => sut.ConfigureColumnOptions(columnOptions, _configurationSectionMock.Object));
}
- private static void AssertColumnSqlOptions(string expectedColumnName, SqlDbType expectedDataType, bool expectedAllowNull, bool expectedNonClusteredIndex, SqlColumn actualColumn)
+ private static void AssertColumnSqlOptions(
+ string expectedColumnName,
+ SqlDbType expectedDataType,
+ bool expectedAllowNull,
+ bool expectedNonClusteredIndex,
+ SqlIndexDirection expectedNonClusteredIndexDirection,
+ SqlColumn actualColumn)
{
Assert.Equal(expectedColumnName, actualColumn.ColumnName);
Assert.Equal(expectedDataType, actualColumn.DataType);
Assert.Equal(expectedAllowNull, actualColumn.AllowNull);
Assert.Equal(expectedNonClusteredIndex, actualColumn.NonClusteredIndex);
+ Assert.Equal(expectedNonClusteredIndexDirection, actualColumn.NonClusteredIndexDirection);
}
private void SetupConfigurationSectionMocks()
@@ -723,7 +758,13 @@ private void SetupConfigurationSectionMocks()
});
}
- private Mock SetupColumnSectionMock(string columnSectionName, string columnName = null, SqlDbType? dataType = null, bool? allowNull = null, bool? nonClusteredIndex = null)
+ private Mock SetupColumnSectionMock(
+ string columnSectionName,
+ string columnName = null,
+ SqlDbType? dataType = null,
+ bool? allowNull = null,
+ bool? nonClusteredIndex = null,
+ SqlIndexDirection? nonClusteredIndexDirection = null)
{
var columnSectionMock = new Mock();
@@ -743,6 +784,10 @@ private Mock SetupColumnSectionMock(string columnSectionN
{
columnSectionMock.Setup(s => s["nonClusteredIndex"]).Returns(nonClusteredIndex.Value.ToString(CultureInfo.InvariantCulture));
}
+ if (nonClusteredIndexDirection != null)
+ {
+ columnSectionMock.Setup(s => s["nonClusteredIndexDirection"]).Returns(nonClusteredIndexDirection.Value.ToString());
+ }
_configurationSectionMock.Setup(s => s.GetSection(columnSectionName)).Returns(columnSectionMock.Object);
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProviderTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProviderTests.cs
index 3cf8eacd..079e429f 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProviderTests.cs
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProviderTests.cs
@@ -28,6 +28,8 @@ public void ConfigureColumnOptionsReadsTraceIdColumnOptions()
_configurationSection.TraceId.ColumnName = columnName;
_configurationSection.TraceId.AllowNull = "false";
_configurationSection.TraceId.DataType = "22"; // VarChar
+ _configurationSection.TraceId.NonClusteredIndex = "true";
+ _configurationSection.TraceId.NonClusteredIndexDirection = "Desc";
var columnOptions = new MSSqlServer.ColumnOptions();
// Act
@@ -37,6 +39,8 @@ public void ConfigureColumnOptionsReadsTraceIdColumnOptions()
Assert.Equal(columnName, columnOptions.TraceId.ColumnName);
Assert.False(columnOptions.TraceId.AllowNull);
Assert.Equal(SqlDbType.VarChar, columnOptions.TraceId.DataType);
+ Assert.True(columnOptions.TraceId.NonClusteredIndex);
+ Assert.Equal(SqlIndexDirection.Desc, columnOptions.TraceId.NonClusteredIndexDirection);
}
[Fact]
@@ -47,6 +51,8 @@ public void ConfigureColumnOptionsReadsSpanIdColumnOptions()
_configurationSection.SpanId.ColumnName = columnName;
_configurationSection.SpanId.AllowNull = "false";
_configurationSection.SpanId.DataType = "22"; // VarChar
+ _configurationSection.SpanId.NonClusteredIndex = "true";
+ _configurationSection.SpanId.NonClusteredIndexDirection = "Desc";
var columnOptions = new MSSqlServer.ColumnOptions();
// Act
@@ -56,6 +62,8 @@ public void ConfigureColumnOptionsReadsSpanIdColumnOptions()
Assert.Equal(columnName, columnOptions.SpanId.ColumnName);
Assert.False(columnOptions.SpanId.AllowNull);
Assert.Equal(SqlDbType.VarChar, columnOptions.SpanId.DataType);
+ Assert.True(columnOptions.SpanId.NonClusteredIndex);
+ Assert.Equal(SqlIndexDirection.Desc, columnOptions.SpanId.NonClusteredIndexDirection);
}
[Fact]
@@ -100,5 +108,30 @@ public void ConfigureColumnOptionsDefaultsAdditionalColumnsResolveHierarchicalPr
additionalColumn1.Should().NotBeNull();
additionalColumn1.ResolveHierarchicalPropertyName.Should().Be(true);
}
+
+ [Fact]
+ public void ConfigureColumnOptionsReadsAdditionalColumnsNonClusteredIndex()
+ {
+ // Arrange
+ const string columnName = "AdditionalColumn1";
+ var columnConfig = new ColumnConfig
+ {
+ ColumnName = columnName,
+ ResolveHierarchicalPropertyName = "false",
+ NonClusteredIndex = "true",
+ NonClusteredIndexDirection = "Desc"
+ };
+ _configurationSection.Columns.Add(columnConfig);
+ var columnOptions = new MSSqlServer.ColumnOptions();
+
+ // Act
+ _sut.ConfigureColumnOptions(_configurationSection, columnOptions);
+
+ // Assert
+ var additionalColumn1 = columnOptions.AdditionalColumns.SingleOrDefault(c => c.ColumnName == columnName);
+ additionalColumn1.Should().NotBeNull();
+ additionalColumn1.NonClusteredIndex.Should().Be(true);
+ additionalColumn1.NonClusteredIndexDirection.Should().Be(SqlIndexDirection.Desc);
+ }
}
}
diff --git a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlCreateTableWriterTests.cs b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlCreateTableWriterTests.cs
index d97909fc..ae255d0f 100644
--- a/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlCreateTableWriterTests.cs
+++ b/test/Serilog.Sinks.MSSqlServer.Tests/Sinks/MSSqlServer/Platform/SqlCreateTableWriterTests.cs
@@ -160,8 +160,58 @@ public void GetSqlCreatesIdNonClusteredIndexColumnsCorrectly()
+ "[IndexCol2] NVARCHAR(50) NOT NULL\r\n"
+ " CONSTRAINT [PK_TestTableName] PRIMARY KEY CLUSTERED ([Id])\r\n"
+ ");\r\n"
- + "CREATE NONCLUSTERED INDEX [IX1_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol1]);\r\n"
- + "CREATE NONCLUSTERED INDEX [IX2_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol2]);\r\n"
+ + "CREATE NONCLUSTERED INDEX [IX1_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol1] Asc);\r\n"
+ + "CREATE NONCLUSTERED INDEX [IX2_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol2] Asc);\r\n"
+ + "END";
+ SetupSut();
+
+ // Act
+ var result = _sut.GetSql();
+
+ // Assert
+ Assert.Contains(expectedResult, result, StringComparison.InvariantCulture);
+ }
+
+ [Fact]
+ public void GetSqlCreatesIdNonClusteredIndexDirectionColumnCorrectly()
+ {
+ // Arrange
+ var sqlColumnId = new SqlColumn { AllowNull = false, ColumnName = "Id", DataType = SqlDbType.Int };
+ _columnOptions = new Serilog.Sinks.MSSqlServer.ColumnOptions { PrimaryKey = sqlColumnId };
+ var dataColumnId = new DataColumn();
+ dataColumnId.ExtendedProperties["SqlColumn"] = sqlColumnId;
+ var dataColumnIndexCol1 = new DataColumn();
+ var sqlColumnIndexCol1 = new SqlColumn
+ {
+ AllowNull = false,
+ ColumnName = "IndexCol1",
+ DataType = SqlDbType.NVarChar,
+ DataLength = 100,
+ NonClusteredIndex = true,
+ NonClusteredIndexDirection = SqlIndexDirection.Desc
+ };
+ dataColumnIndexCol1.ExtendedProperties["SqlColumn"] = sqlColumnIndexCol1;
+ var dataColumnIndexCol2 = new DataColumn();
+ var sqlColumnIndexCol2 = new SqlColumn
+ {
+ AllowNull = false,
+ ColumnName = "IndexCol2",
+ DataType = SqlDbType.NVarChar,
+ DataLength = 50,
+ NonClusteredIndex = true
+ };
+ dataColumnIndexCol2.ExtendedProperties["SqlColumn"] = sqlColumnIndexCol2;
+ _dataTable.Columns.Add(dataColumnId);
+ _dataTable.Columns.Add(dataColumnIndexCol1);
+ _dataTable.Columns.Add(dataColumnIndexCol2);
+ var expectedResult = "CREATE TABLE [TestSchemaName].[TestTableName] ( \r\n"
+ + "[Id] INT NOT NULL,\r\n"
+ + "[IndexCol1] NVARCHAR(100) NOT NULL,\r\n"
+ + "[IndexCol2] NVARCHAR(50) NOT NULL\r\n"
+ + " CONSTRAINT [PK_TestTableName] PRIMARY KEY CLUSTERED ([Id])\r\n"
+ + ");\r\n"
+ + "CREATE NONCLUSTERED INDEX [IX1_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol1] Desc);\r\n"
+ + "CREATE NONCLUSTERED INDEX [IX2_TestTableName] ON [TestSchemaName].[TestTableName] ([IndexCol2] Asc);\r\n"
+ "END";
SetupSut();