Skip to content

Commit 8374d9a

Browse files
Settings cleanup and improvements (#1901)
* Make initialization of setings less error prone * Throw expression * Unneeded nullable * Range * Remove no longer needed log error --------- Co-authored-by: Daniel Marbach <danielmarbach@users.noreply.github.com>
1 parent 2e42d9d commit 8374d9a

File tree

9 files changed

+44
-78
lines changed

9 files changed

+44
-78
lines changed

src/ScriptBuilder.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public class ScriptGenerator
8888
{
8989
public ScriptGenerator(string assemblyPath, string destinationDirectory, bool clean = true, bool overwrite = true, System.Collections.Generic.IReadOnlyList<NServiceBus.Persistence.Sql.ScriptBuilder.BuildSqlDialect>? dialectOptions = null, System.Func<string, string>? promotionFinder = null) { }
9090
public void Generate() { }
91-
public static void Generate(string assemblyPath, string targetDirectory, System.Action<string, string> logError, System.Func<string, string> promotionPathFinder) { }
91+
public static void Generate(string assemblyPath, string targetDirectory, System.Func<string, string> promotionPathFinder) { }
9292
}
9393
public abstract class ScriptWriter
9494
{

src/ScriptBuilder.Tests/SqlAttributeParametersReadersTest.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public void Minimal()
1515
//At least one is required
1616
"MsSqlServerScripts", true
1717
}
18-
});
18+
}, []);
1919

2020
Assert.That(result, Is.Not.Null);
2121
Approver.Verify(result);
@@ -24,7 +24,7 @@ public void Minimal()
2424
[Test]
2525
public void Defaults()
2626
{
27-
var result = SettingsAttributeReader.ReadFromProperties([]);
27+
var result = SettingsAttributeReader.ReadFromProperties([], []);
2828
Assert.That(result, Is.Not.Null);
2929
Approver.Verify(result);
3030
}
@@ -59,10 +59,8 @@ public void NonDefaults()
5959
{
6060
"ProduceOutboxScripts", false
6161
}
62-
});
62+
}, []);
6363
Assert.That(result, Is.Not.Null);
6464
Approver.Verify(result);
6565
}
66-
67-
6866
}

src/ScriptBuilder/AttributeReading/Settings.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
using System.Collections.Generic;
44
using NServiceBus.Persistence.Sql.ScriptBuilder;
55

6-
class Settings
6+
sealed class Settings
77
{
8-
public List<BuildSqlDialect> BuildDialects { get; set; } = [];
8+
public required IReadOnlyCollection<BuildSqlDialect> BuildDialects { get; init; }
99

10-
public string? ScriptPromotionPath { get; set; }
10+
public string? ScriptPromotionPath { get; init; }
1111

12-
public bool ProduceOutboxScripts { get; set; }
12+
public required bool ProduceOutboxScripts { get; init; }
1313

14-
public bool ProduceSubscriptionScripts { get; set; }
14+
public required bool ProduceSubscriptionScripts { get; init; }
1515

16-
public bool ProduceTimeoutScripts { get; set; }
16+
public required bool ProduceTimeoutScripts { get; init; }
1717

18-
public bool ProduceSagaScripts { get; set; }
18+
public required bool ProduceSagaScripts { get; init; }
1919

20-
public List<SagaDefinition> SagaDefinitions { get; set; } = [];
20+
public required IReadOnlyCollection<SagaDefinition> SagaDefinitions { get; init; }
2121
}

src/ScriptBuilder/AttributeReading/SettingsAttributeReader.cs

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ static class SettingsAttributeReader
1313
{
1414
public static Settings Read(string assemblyPath)
1515
{
16-
Settings? settings = null;
17-
List<SagaDefinition> sagas = [];
16+
List<SagaDefinition> sagaDefinitions = [];
17+
Dictionary<string, object?>? properties = null;
1818

1919
using var stream = File.OpenRead(assemblyPath);
2020
using var peReader = new PEReader(stream);
@@ -37,8 +37,12 @@ public static Settings Read(string assemblyPath)
3737
if (attNamespace == "NServiceBus.Persistence.Sql")
3838
{
3939
var args = attribute.DecodeValue(AttributeTypeProvider.Instance);
40-
var properties = args.NamedArguments.ToDictionary(o => o.Name!, o => o.Value);
41-
settings = ReadFromProperties(properties);
40+
// This assembly level attribute can only exists once per assembly. This is an additional safe guard to make this code more intention revealing
41+
if (properties is not null)
42+
{
43+
throw new InvalidOperationException("Only one SqlPersistenceSettingsAttribute can be defined per assembly");
44+
}
45+
properties = args.NamedArguments.ToDictionary(o => o.Name!, o => o.Value);
4246
}
4347
}
4448
}
@@ -54,48 +58,43 @@ public static Settings Read(string assemblyPath)
5458
if (string.IsNullOrEmpty(attNamespace))
5559
{
5660
var args = attribute.DecodeValue(AttributeTypeProvider.Instance);
57-
var properties = args.NamedArguments.ToDictionary(o => o.Name!, o => o.Value);
61+
var sagaProperties = args.NamedArguments.ToDictionary(o => o.Name!, o => o.Value);
5862

59-
var sagaType = properties.GetValueOrDefault("SagaType") as string;
60-
var corrName = properties.GetValueOrDefault("CorrelationPropertyName") as string;
61-
var corrType = properties.GetValueOrDefault("CorrelationPropertyType") as string;
62-
var transName = properties.GetValueOrDefault("TransitionalCorrelationPropertyName") as string;
63-
var transType = properties.GetValueOrDefault("TransitionalCorrelationPropertyType") as string;
64-
var tableSuffix = properties.GetValueOrDefault("TableSuffix") as string;
63+
var sagaType = sagaProperties.GetValueOrDefault("SagaType") as string;
64+
var corrName = sagaProperties.GetValueOrDefault("CorrelationPropertyName") as string;
65+
var corrType = sagaProperties.GetValueOrDefault("CorrelationPropertyType") as string;
66+
var transName = sagaProperties.GetValueOrDefault("TransitionalCorrelationPropertyName") as string;
67+
var transType = sagaProperties.GetValueOrDefault("TransitionalCorrelationPropertyType") as string;
68+
var tableSuffix = sagaProperties.GetValueOrDefault("TableSuffix") as string;
6569

6670
var correlation = GetCorrelation(corrName, corrType);
6771
var transitionalCorrelation = GetCorrelation(transName, transType);
6872

6973
if (tableSuffix is not null && sagaType is not null)
7074
{
7175
var definition = new SagaDefinition(tableSuffix, sagaType, correlation, transitionalCorrelation);
72-
sagas.Add(definition);
76+
sagaDefinitions.Add(definition);
7377
}
7478
}
7579
}
7680
}
7781
}
7882

7983
// If no attribute, generate the default
80-
settings ??= ReadFromProperties([]);
81-
82-
// Then apply the saga definitions
83-
settings.SagaDefinitions = sagas;
84-
return settings;
84+
return ReadFromProperties(properties ?? [], sagaDefinitions);
8585
}
8686

87-
public static Settings ReadFromProperties(Dictionary<string, object?> properties)
88-
{
89-
return new Settings
87+
public static Settings ReadFromProperties(Dictionary<string, object?> properties, IReadOnlyCollection<SagaDefinition> sagas) =>
88+
new()
9089
{
9190
BuildDialects = ReadBuildDialects(properties).ToList(),
9291
ScriptPromotionPath = ReadScriptPromotionPath(properties),
9392
ProduceSagaScripts = GetBoolProperty(properties, "ProduceSagaScripts", true),
9493
ProduceTimeoutScripts = GetBoolProperty(properties, "ProduceTimeoutScripts", true),
9594
ProduceSubscriptionScripts = GetBoolProperty(properties, "ProduceSubscriptionScripts", true),
9695
ProduceOutboxScripts = GetBoolProperty(properties, "ProduceOutboxScripts", true),
96+
SagaDefinitions = sagas
9797
};
98-
}
9998

10099
static bool GetBoolProperty(Dictionary<string, object?> properties, string key, bool defaultValue = false)
101100
=> properties.GetValueOrDefault(key) as bool? ?? defaultValue;
@@ -157,12 +156,7 @@ static IEnumerable<BuildSqlDialect> ReadBuildDialects(Dictionary<string, object?
157156
return null;
158157
}
159158

160-
if (!Enum.TryParse<CorrelationPropertyType>(type, out var propType))
161-
{
162-
throw new Exception($"Invalid correlation property type '{type}' found in metadata attribute.");
163-
}
164-
165-
return new CorrelationProperty(name, propType);
159+
return !Enum.TryParse<CorrelationPropertyType>(type, out var propType) ? throw new Exception($"Invalid correlation property type '{type}' found in metadata attribute.") : new CorrelationProperty(name, propType);
166160
}
167161

168162
// Minimal implementation that only supports primitive types
@@ -172,8 +166,8 @@ sealed class AttributeTypeProvider : ICustomAttributeTypeProvider<object?>
172166

173167
public static readonly AttributeTypeProvider Instance = new();
174168

175-
public object? GetPrimitiveType(PrimitiveTypeCode typeCode) => typeCode;
176-
public object? GetSystemType() => typeof(Type);
169+
public object GetPrimitiveType(PrimitiveTypeCode typeCode) => typeCode;
170+
public object GetSystemType() => typeof(Type);
177171
public object? GetTypeFromDefinition(MetadataReader r, TypeDefinitionHandle h, byte raw) => null;
178172
public object? GetTypeFromReference(MetadataReader r, TypeReferenceHandle h, byte raw) => null;
179173
public object? GetSZArrayType(object? elementType) => null;

src/ScriptBuilder/ScriptGenerator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ public class ScriptGenerator(string assemblyPath,
1414
IReadOnlyList<BuildSqlDialect>? dialectOptions = null,
1515
Func<string, string>? promotionFinder = null)
1616
{
17-
public static void Generate(string assemblyPath, string targetDirectory,
18-
Action<string, string> logError, Func<string, string> promotionPathFinder)
17+
public static void Generate(string assemblyPath, string targetDirectory, Func<string, string> promotionPathFinder)
1918
{
2019
var writer = new ScriptGenerator(assemblyPath, targetDirectory, promotionFinder: promotionPathFinder);
2120
writer.Generate();

src/ScriptBuilder/Writers/SagaWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class SagaWriter(bool clean,
88
bool overwrite,
99
string scriptPath,
10-
List<SagaDefinition> sagaDefinitions)
10+
IReadOnlyCollection<SagaDefinition> sagaDefinitions)
1111
: ScriptWriter(clean, overwrite, scriptPath)
1212
{
1313
public override void WriteScripts(BuildSqlDialect dialect)
@@ -21,7 +21,7 @@ public override void WriteScripts(BuildSqlDialect dialect)
2121
var maximumNameLength = 244 - ScriptPath.Length;
2222
if (sagaFileName.Length > maximumNameLength)
2323
{
24-
sagaFileName = $"{sagaFileName.Substring(0, maximumNameLength)}_{index}";
24+
sagaFileName = $"{sagaFileName[..maximumNameLength]}_{index}";
2525
index++;
2626
}
2727

src/ScriptBuilderTask.Tests/InnerTaskTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.IO;
1+
using System.IO;
32
using System.Linq;
43
using NUnit.Framework;
54
using Particular.Approvals;
@@ -25,8 +24,7 @@ public void IntegrationTest()
2524
assemblyPath: Path.Combine(testDirectory, "ScriptBuilderTask.Tests.Target.dll"),
2625
intermediateDirectory: intermediatePath,
2726
projectDirectory: "TheProjectDir",
28-
solutionDirectory: Path.Combine(temp, "PromotePath"),
29-
logError: (error, type) => throw new Exception(error));
27+
solutionDirectory: Path.Combine(temp, "PromotePath"));
3028
innerTask.Execute();
3129
var files = Directory.EnumerateFiles(temp, "*.*", SearchOption.AllDirectories)
3230
.Select(s => s.Replace(temp, "temp").ConvertPathSeparators("/"))

src/ScriptBuilderTask/InnerTask.cs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
1-
using System;
2-
using System.IO;
1+
using System.IO;
32
using NServiceBus.Persistence.Sql;
43

5-
class InnerTask
4+
sealed class InnerTask(string assemblyPath, string intermediateDirectory, string projectDirectory, string solutionDirectory)
65
{
7-
string assemblyPath;
8-
string intermediateDirectory;
9-
string projectDirectory;
10-
string solutionDirectory;
11-
Action<string, string> logError;
12-
13-
public InnerTask(string assemblyPath, string intermediateDirectory, string projectDirectory, string solutionDirectory, Action<string, string> logError)
14-
{
15-
this.assemblyPath = assemblyPath;
16-
this.intermediateDirectory = intermediateDirectory;
17-
this.projectDirectory = projectDirectory;
18-
this.solutionDirectory = solutionDirectory;
19-
this.logError = logError;
20-
}
21-
22-
public void Execute()
23-
{
24-
ScriptGenerator.Generate(assemblyPath, intermediateDirectory, logError, FindPromotionPath);
25-
}
6+
public void Execute() => ScriptGenerator.Generate(assemblyPath, intermediateDirectory, FindPromotionPath);
267

278
string FindPromotionPath(string promotionPath)
289
{

src/ScriptBuilderTask/SqlPersistenceScriptBuilderTask.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ public override bool Execute()
3434
try
3535
{
3636
ValidateInputs();
37-
var innerTask = new InnerTask(AssemblyPath, IntermediateDirectory, ProjectDirectory, SolutionDirectory,
38-
logError: (error, file) =>
39-
{
40-
logger.LogError(error, file);
41-
});
37+
var innerTask = new InnerTask(AssemblyPath, IntermediateDirectory, ProjectDirectory, SolutionDirectory);
4238
innerTask.Execute();
4339
}
4440
catch (ErrorsException exception)

0 commit comments

Comments
 (0)