Skip to content

Commit 44c7bb3

Browse files
committed
Add fixes and tests
* Added unit tests for MicrosoftExtensionsColumnOptionsProvider, MicrosoftExtensionsColumnOptionsProviderTests and SqlCreateTableWriter * Added SetProperty.IfEnumNotNull<T>() and SetProperty.IfEnumProvided<T>() to handle enum column option reading from configuration * Use NonClusteredIndexDirection in WorkerServiceDemo Related issue: #636
1 parent 6509078 commit 44c7bb3

File tree

9 files changed

+210
-28
lines changed

9 files changed

+210
-28
lines changed

sample/WorkerServiceDemo/appsettings.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
"removeStandardColumns": [ "MessageTemplate", "Properties" ],
2626
"timeStamp": {
2727
"columnName": "Timestamp",
28-
"convertToUtc": false
28+
"allowNulls": false,
29+
"convertToUtc": false,
30+
"nonClusteredIndex": true,
31+
"nonClusteredIndexDirection": "Desc"
2932
},
3033
"customColumns": [
3134
{
@@ -43,6 +46,12 @@
4346
"propertyName": "NonstructuredProperty.WithNameContainingDots.Name",
4447
"resolveHierarchicalPropertyName": false,
4548
"dataType": "12"
49+
},
50+
{
51+
"columnName": "AdditionalColumn4",
52+
"dataType": "0",
53+
"nonClusteredIndex": true,
54+
"nonClusteredIndexDirection": "Desc"
4655
}
4756
]
4857
},

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ private static void SetCommonColumnOptions(IConfigurationSection section, SqlCol
3434
SetProperty.IfNotNull<bool>(section["allowNull"], (val) => target.AllowNull = val);
3535
SetProperty.IfNotNull<int>(section["dataLength"], (val) => target.DataLength = val);
3636
SetProperty.IfNotNull<bool>(section["nonClusteredIndex"], (val) => target.NonClusteredIndex = val);
37-
SetProperty.IfNotNull<SqlIndexDirection>(section["nonClusteredIndexDirection"], (val) => target.NonClusteredIndexDirection = val);
37+
SetProperty.IfEnumNotNull<SqlIndexDirection>(section["nonClusteredIndexDirection"],
38+
(val) => target.NonClusteredIndexDirection = val);
3839
}
3940

4041
private static void AddRemoveStandardColumns(IConfigurationSection config, ColumnOptions columnOptions)

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,25 @@ public static void IfNotNull<T>(string value, PropertySetter<T> setter)
2929
var setting = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
3030
setter(setting);
3131
}
32-
// don't change the property if the conversion fails
32+
// don't change the property if the conversion fails
33+
catch (InvalidCastException) { }
34+
catch (OverflowException) { }
35+
}
36+
37+
/// <summary>
38+
/// This will only set a value (execute the PropertySetter delegate) if the value is non-null.
39+
/// It also converts the provided value to the requested enum type. This allows configuration to only
40+
/// apply property changes when external configuration has actually provided a value.
41+
/// </summary>
42+
public static void IfEnumNotNull<T>(string value, PropertySetter<T> setter) where T : System.Enum
43+
{
44+
if (value == null || setter == null) return;
45+
try
46+
{
47+
var setting = (T)Enum.Parse(typeof(T), value, ignoreCase: true);
48+
setter(setting);
49+
}
50+
// don't change the property if the conversion fails
3351
catch (InvalidCastException) { }
3452
catch (OverflowException) { }
3553
}

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ public string NonClusteredIndex
8383
set { this[nameof(NonClusteredIndex)] = value; }
8484
}
8585

86+
[ConfigurationProperty("NonClusteredIndexDirection")]
87+
public string NonClusteredIndexDirection
88+
{
89+
get { return (string)this[nameof(NonClusteredIndexDirection)]; }
90+
set { this[nameof(NonClusteredIndexDirection)] = value; }
91+
}
92+
8693
internal SqlColumn AsSqlColumn()
8794
{
8895
var sqlColumn = new SqlColumn();
@@ -106,6 +113,9 @@ internal SqlColumn AsSqlColumn()
106113

107114
SetProperty.IfProvided<bool>(this, nameof(NonClusteredIndex), (val) => sqlColumn.NonClusteredIndex = val);
108115

116+
SetProperty.IfEnumProvided<SqlIndexDirection>(this, nameof(NonClusteredIndexDirection),
117+
(val) => sqlColumn.NonClusteredIndexDirection = val);
118+
109119
return sqlColumn;
110120
}
111121
}

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ public static void IfProvided<T>(ConfigurationElement element, string propertyNa
2222
IfNotNull((string)property.Value, setter);
2323
}
2424

25+
/// <summary>
26+
/// Test the underlying enum property collection's value-origin flag for a non-default string value. Empty strings allowed.
27+
/// </summary>
28+
public static void IfEnumProvided<T>(ConfigurationElement element, string propertyName, PropertySetter<T> setter)
29+
where T : System.Enum
30+
{
31+
if (element == null)
32+
{
33+
return;
34+
}
35+
36+
var property = element.ElementInformation.Properties[propertyName];
37+
if (property.ValueOrigin == PropertyValueOrigin.Default) return;
38+
IfEnumNotNull((string)property.Value, setter);
39+
}
40+
2541
/// <summary>
2642
/// Test the underlying property collection's value-origin flag for a non-default, non-null, non-empty string value.
2743
/// </summary>

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ private static void SetCommonColumnOptions(ColumnConfig source, SqlColumn target
2626
SetProperty.IfProvided<bool>(source, nameof(target.AllowNull), value => target.AllowNull = value);
2727
SetProperty.IfProvided<int>(source, nameof(target.DataLength), value => target.DataLength = value);
2828
SetProperty.IfProvided<bool>(source, nameof(target.NonClusteredIndex), value => target.NonClusteredIndex = value);
29-
SetProperty.IfProvided<SqlIndexDirection>(source, nameof(target.NonClusteredIndexDirection), value => target.NonClusteredIndexDirection = value);
29+
SetProperty.IfEnumProvided<SqlIndexDirection>(source, nameof(target.NonClusteredIndexDirection), value => target.NonClusteredIndexDirection = value);
3030
}
3131

3232
private static void ReadPropertiesColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions)

test/Serilog.Sinks.MSSqlServer.Tests/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProviderTests.cs

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,16 @@ public void ConfigureColumnOptionsAddsColumnIdWithSpecifiedOptions()
105105
var dataType = SqlDbType.Bit;
106106
var allowNull = false;
107107
var nonClusteredIndex = true;
108+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
108109
SetupConfigurationSectionMocks();
109-
SetupColumnSectionMock("id", columnName, dataType, allowNull, nonClusteredIndex);
110+
SetupColumnSectionMock("id", columnName, dataType, allowNull, nonClusteredIndex, nonClusteredIndexDirection);
110111
var sut = new MicrosoftExtensionsColumnOptionsProvider();
111112

112113
// Act
113114
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
114115

115116
// Assert
116-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Id);
117+
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, nonClusteredIndexDirection, result.Id);
117118
}
118119

119120
[Fact]
@@ -157,15 +158,18 @@ public void ConfigureColumnOptionsAddsColumnLevelWithSpecifiedOptions()
157158
var dataType = SqlDbType.Bit;
158159
var allowNull = true;
159160
var nonClusteredIndex = true;
161+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
160162
SetupConfigurationSectionMocks();
161-
SetupColumnSectionMock("level", columnName, dataType, allowNull, nonClusteredIndex);
163+
SetupColumnSectionMock("level", columnName, dataType, allowNull,
164+
nonClusteredIndex, nonClusteredIndexDirection);
162165
var sut = new MicrosoftExtensionsColumnOptionsProvider();
163166

164167
// Act
165168
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
166169

167170
// Assert
168-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Level);
171+
AssertColumnSqlOptions(columnName, dataType, allowNull,
172+
nonClusteredIndex, nonClusteredIndexDirection, result.Level);
169173
}
170174

171175
[Fact]
@@ -192,15 +196,18 @@ public void ConfigureColumnOptionsAddsColumnPropertiesWithSpecifiedOptions()
192196
var dataType = SqlDbType.Bit;
193197
var allowNull = true;
194198
var nonClusteredIndex = true;
199+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
195200
SetupConfigurationSectionMocks();
196-
SetupColumnSectionMock("properties", columnName, dataType, allowNull, nonClusteredIndex);
201+
SetupColumnSectionMock("properties", columnName, dataType, allowNull,
202+
nonClusteredIndex, nonClusteredIndexDirection);
197203
var sut = new MicrosoftExtensionsColumnOptionsProvider();
198204

199205
// Act
200206
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
201207

202208
// Assert
203-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Properties);
209+
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex,
210+
nonClusteredIndexDirection, result.Properties);
204211
}
205212

206213
[Fact]
@@ -410,15 +417,18 @@ public void ConfigureColumnOptionsAddsColumnTimeStampWithSpecifiedOptions()
410417
var dataType = SqlDbType.Bit;
411418
var allowNull = true;
412419
var nonClusteredIndex = true;
420+
var nonClusteredIndexDirection = SqlIndexDirection.Desc;
413421
SetupConfigurationSectionMocks();
414-
SetupColumnSectionMock("timeStamp", columnName, dataType, allowNull, nonClusteredIndex);
422+
SetupColumnSectionMock("timeStamp", columnName, dataType, allowNull,
423+
nonClusteredIndex, nonClusteredIndexDirection);
415424
var sut = new MicrosoftExtensionsColumnOptionsProvider();
416425

417426
// Act
418427
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
419428

420429
// Assert
421-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.TimeStamp);
430+
AssertColumnSqlOptions(columnName, dataType, allowNull,
431+
nonClusteredIndex, nonClusteredIndexDirection, result.TimeStamp);
422432
}
423433

424434
[Fact]
@@ -445,15 +455,18 @@ public void ConfigureColumnOptionsAddsColumnLogEventWithSpecifiedOptions()
445455
var dataType = SqlDbType.Bit;
446456
var allowNull = true;
447457
var nonClusteredIndex = true;
458+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
448459
SetupConfigurationSectionMocks();
449-
SetupColumnSectionMock("logEvent", columnName, dataType, allowNull, nonClusteredIndex);
460+
SetupColumnSectionMock("logEvent", columnName, dataType, allowNull,
461+
nonClusteredIndex, nonClusteredIndexDirection);
450462
var sut = new MicrosoftExtensionsColumnOptionsProvider();
451463

452464
// Act
453465
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
454466

455467
// Assert
456-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.LogEvent);
468+
AssertColumnSqlOptions(columnName, dataType, allowNull,
469+
nonClusteredIndex, nonClusteredIndexDirection, result.LogEvent);
457470
}
458471

459472
[Fact]
@@ -496,15 +509,18 @@ public void ConfigureColumnOptionsAddsColumnTraceIdWithSpecifiedOptions()
496509
var dataType = SqlDbType.NVarChar;
497510
var allowNull = true;
498511
var nonClusteredIndex = true;
512+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
499513
SetupConfigurationSectionMocks();
500-
SetupColumnSectionMock("traceId", columnName, dataType, allowNull, nonClusteredIndex);
514+
SetupColumnSectionMock("traceId", columnName, dataType, allowNull,
515+
nonClusteredIndex, nonClusteredIndexDirection);
501516
var sut = new MicrosoftExtensionsColumnOptionsProvider();
502517

503518
// Act
504519
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
505520

506521
// Assert
507-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.TraceId);
522+
AssertColumnSqlOptions(columnName, dataType, allowNull,
523+
nonClusteredIndex, nonClusteredIndexDirection, result.TraceId);
508524
}
509525

510526
[Fact]
@@ -515,15 +531,18 @@ public void ConfigureColumnOptionsAddsColumnSpanIdWithSpecifiedOptions()
515531
var dataType = SqlDbType.NVarChar;
516532
var allowNull = true;
517533
var nonClusteredIndex = true;
534+
var nonClusteredIndexDirection = SqlIndexDirection.Desc;
518535
SetupConfigurationSectionMocks();
519-
SetupColumnSectionMock("spanId", columnName, dataType, allowNull, nonClusteredIndex);
536+
SetupColumnSectionMock("spanId", columnName, dataType, allowNull,
537+
nonClusteredIndex, nonClusteredIndexDirection);
520538
var sut = new MicrosoftExtensionsColumnOptionsProvider();
521539

522540
// Act
523541
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
524542

525543
// Assert
526-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.SpanId);
544+
AssertColumnSqlOptions(columnName, dataType, allowNull,
545+
nonClusteredIndex, nonClusteredIndexDirection, result.SpanId);
527546
}
528547

529548
[Fact]
@@ -534,15 +553,18 @@ public void ConfigureColumnOptionsAddsColumnMessageWithSpecifiedOptions()
534553
var dataType = SqlDbType.Bit;
535554
var allowNull = true;
536555
var nonClusteredIndex = true;
556+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
537557
SetupConfigurationSectionMocks();
538-
SetupColumnSectionMock("message", columnName, dataType, allowNull, nonClusteredIndex);
558+
SetupColumnSectionMock("message", columnName, dataType, allowNull,
559+
nonClusteredIndex, nonClusteredIndexDirection);
539560
var sut = new MicrosoftExtensionsColumnOptionsProvider();
540561

541562
// Act
542563
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
543564

544565
// Assert
545-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Message);
566+
AssertColumnSqlOptions(columnName, dataType, allowNull,
567+
nonClusteredIndex, nonClusteredIndexDirection, result.Message);
546568
}
547569

548570
[Fact]
@@ -553,15 +575,18 @@ public void ConfigureColumnOptionsAddsColumnExceptionWithSpecifiedOptions()
553575
var dataType = SqlDbType.Bit;
554576
var allowNull = true;
555577
var nonClusteredIndex = true;
578+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
556579
SetupConfigurationSectionMocks();
557-
SetupColumnSectionMock("exception", columnName, dataType, allowNull, nonClusteredIndex);
580+
SetupColumnSectionMock("exception", columnName, dataType, allowNull,
581+
nonClusteredIndex, nonClusteredIndexDirection);
558582
var sut = new MicrosoftExtensionsColumnOptionsProvider();
559583

560584
// Act
561585
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
562586

563587
// Assert
564-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.Exception);
588+
AssertColumnSqlOptions(columnName, dataType, allowNull,
589+
nonClusteredIndex, nonClusteredIndexDirection, result.Exception);
565590
}
566591

567592
[Fact]
@@ -572,15 +597,18 @@ public void ConfigureColumnOptionsAddsColumnMessageTemplateWithSpecifiedOptions(
572597
var dataType = SqlDbType.Bit;
573598
var allowNull = true;
574599
var nonClusteredIndex = true;
600+
var nonClusteredIndexDirection = SqlIndexDirection.Asc;
575601
SetupConfigurationSectionMocks();
576-
SetupColumnSectionMock("messageTemplate", columnName, dataType, allowNull, nonClusteredIndex);
602+
SetupColumnSectionMock("messageTemplate", columnName, dataType, allowNull,
603+
nonClusteredIndex, nonClusteredIndexDirection);
577604
var sut = new MicrosoftExtensionsColumnOptionsProvider();
578605

579606
// Act
580607
var result = sut.ConfigureColumnOptions(new Serilog.Sinks.MSSqlServer.ColumnOptions(), _configurationSectionMock.Object);
581608

582609
// Assert
583-
AssertColumnSqlOptions(columnName, dataType, allowNull, nonClusteredIndex, result.MessageTemplate);
610+
AssertColumnSqlOptions(columnName, dataType, allowNull,
611+
nonClusteredIndex, nonClusteredIndexDirection, result.MessageTemplate);
584612
}
585613

586614
[Fact]
@@ -697,12 +725,19 @@ public void ConfigureColumnOptionsThrowsWhenSettingPrimaryKeyColumnNameToUndefin
697725
Assert.Throws<ArgumentException>(() => sut.ConfigureColumnOptions(columnOptions, _configurationSectionMock.Object));
698726
}
699727

700-
private static void AssertColumnSqlOptions(string expectedColumnName, SqlDbType expectedDataType, bool expectedAllowNull, bool expectedNonClusteredIndex, SqlColumn actualColumn)
728+
private static void AssertColumnSqlOptions(
729+
string expectedColumnName,
730+
SqlDbType expectedDataType,
731+
bool expectedAllowNull,
732+
bool expectedNonClusteredIndex,
733+
SqlIndexDirection expectedNonClusteredIndexDirection,
734+
SqlColumn actualColumn)
701735
{
702736
Assert.Equal(expectedColumnName, actualColumn.ColumnName);
703737
Assert.Equal(expectedDataType, actualColumn.DataType);
704738
Assert.Equal(expectedAllowNull, actualColumn.AllowNull);
705739
Assert.Equal(expectedNonClusteredIndex, actualColumn.NonClusteredIndex);
740+
Assert.Equal(expectedNonClusteredIndexDirection, actualColumn.NonClusteredIndexDirection);
706741
}
707742

708743
private void SetupConfigurationSectionMocks()
@@ -723,7 +758,13 @@ private void SetupConfigurationSectionMocks()
723758
});
724759
}
725760

726-
private Mock<IConfigurationSection> SetupColumnSectionMock(string columnSectionName, string columnName = null, SqlDbType? dataType = null, bool? allowNull = null, bool? nonClusteredIndex = null)
761+
private Mock<IConfigurationSection> SetupColumnSectionMock(
762+
string columnSectionName,
763+
string columnName = null,
764+
SqlDbType? dataType = null,
765+
bool? allowNull = null,
766+
bool? nonClusteredIndex = null,
767+
SqlIndexDirection? nonClusteredIndexDirection = null)
727768
{
728769
var columnSectionMock = new Mock<IConfigurationSection>();
729770

@@ -743,6 +784,10 @@ private Mock<IConfigurationSection> SetupColumnSectionMock(string columnSectionN
743784
{
744785
columnSectionMock.Setup(s => s["nonClusteredIndex"]).Returns(nonClusteredIndex.Value.ToString(CultureInfo.InvariantCulture));
745786
}
787+
if (nonClusteredIndexDirection != null)
788+
{
789+
columnSectionMock.Setup(s => s["nonClusteredIndexDirection"]).Returns(nonClusteredIndexDirection.Value.ToString());
790+
}
746791

747792
_configurationSectionMock.Setup(s => s.GetSection(columnSectionName)).Returns(columnSectionMock.Object);
748793

0 commit comments

Comments
 (0)