Skip to content

Commit 99d1941

Browse files
authored
chore: Modernize code base with latest C# features (#69)
* chore: Modernize code base with latest C# features * Resolve nullability warnings * Convert != null checks to pattern matching or null-coalescing operators * Use expression-bodied members where appropriate * Apply target-typed new expressions * Use primary constructors where beneficial * Apply throw expressions in getters/setters * Use pattern matching in conditional logic * Simplify property getters/setters No changes to code logic Update dependencies to latest versions
1 parent 77622d9 commit 99d1941

53 files changed

Lines changed: 448 additions & 797 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ jobs:
4848
run: dotnet add ./MailMergeLib.Tests/MailMergeLib.Tests.csproj package AltCover
4949

5050
- name: Build solution
51-
run: dotnet build MailMergeLib.sln /verbosity:minimal /t:rebuild /p:configuration=release /nowarn:CS1591,CS0618
51+
run: dotnet build MailMergeLib.slnx /verbosity:minimal /t:rebuild /p:configuration=release /nowarn:CS1591,CS0618
5252

5353
- name: Run tests
54-
run: dotnet test --framework net10.0 --no-build --configuration release MailMergeLib.sln /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverStrongNameKey="../MailMergeLib/MailMergeLib.snk" /p:AltCoverAssemblyExcludeFilter="MailMergeLib.Tests|NUnit3.TestAdapter" /p:AltCoverLineCover="true"
54+
run: dotnet test --framework net10.0 --no-build --configuration release MailMergeLib.slnx /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverStrongNameKey="../MailMergeLib/MailMergeLib.snk" /p:AltCoverAssemblyFilter="MailMergeLib" /p:AltCoverLineCover="true"
5555

5656
- name: Upload coverage to Codecov
5757
uses: codecov/codecov-action@v7
@@ -110,13 +110,14 @@ jobs:
110110
echo "File version: $versionFile"
111111
112112
- name: Build solution
113-
run: dotnet build MailMergeLib.sln /verbosity:minimal /t:rebuild /p:configuration=release /p:IncludeSymbols=true /p:ContinuousIntegrationBuild=true /p:Version=${{ steps.version.outputs.VERSION }} /p:FileVersion=${{ steps.version.outputs.FILE_VERSION }}
114-
113+
run: dotnet build MailMergeLib.slnx /verbosity:minimal /t:rebuild /p:configuration=release /p:IncludeSymbols=true /p:ContinuousIntegrationBuild=true /p:Version=${{ steps.version.outputs.VERSION }} /p:FileVersion=${{ steps.version.outputs.FILE_VERSION }}
114+
115115
- name: Run tests (net462)
116-
run: dotnet test --framework net462 --no-build --configuration release MailMergeLib.sln /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverStrongNameKey="..\MailMergeLib\MailMergeLib.snk" /p:AltCoverAssemblyExcludeFilter="MailMergeLib.Tests|NUnit3.TestAdapter" /p:AltCoverLineCover="true"
116+
# AltCover fails on .NET Framework with stack overflow issue, so exclude coverage for now
117+
run: dotnet test --framework net462 --no-build --configuration release MailMergeLib.slnx
117118

118119
- name: Run tests (net10.0)
119-
run: dotnet test --framework net10.0 --no-build --configuration release MailMergeLib.sln /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverStrongNameKey="..\MailMergeLib\MailMergeLib.snk" /p:AltCoverAssemblyExcludeFilter="MailMergeLib.Tests|NUnit3.TestAdapter" /p:AltCoverLineCover="true"
120+
run: dotnet test --framework net10.0 --no-build --configuration release MailMergeLib.slnx /p:AltCover=true /p:AltCoverXmlReport="coverage.xml" /p:AltCoverStrongNameKey="..\MailMergeLib\MailMergeLib.snk" /p:AltCoverAssemblyFilter="MailMergeLib" /p:AltCoverLineCover="true"
120121

121122
- name: Upload coverage to Codecov
122123
uses: codecov/codecov-action@v7
@@ -127,7 +128,7 @@ jobs:
127128
token: ${{ secrets.CODECOV_TOKEN }}
128129

129130
- name: Pack NuGet packages
130-
run: dotnet pack MailMergeLib.sln --verbosity minimal --no-build --configuration release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:PackageOutputPath=${{ github.workspace }}/artifacts /p:ContinuousIntegrationBuild=true
131+
run: dotnet pack MailMergeLib.slnx --verbosity minimal --no-build --configuration release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:PackageOutputPath=${{ github.workspace }}/artifacts /p:ContinuousIntegrationBuild=true
131132

132133
- name: Upload NuGet packages as artifacts
133134
uses: actions/upload-artifact@v7

Src/.editorconfig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ csharp_prefer_braces = false:suggestion
8484
#Style - expression bodied member options
8585

8686
#prefer block bodies for accessors
87-
csharp_style_expression_bodied_accessors = false:suggestion
87+
csharp_style_expression_bodied_accessors = true:suggestion
8888
#prefer block bodies for constructors
89-
csharp_style_expression_bodied_constructors = false:suggestion
89+
csharp_style_expression_bodied_constructors = true:suggestion
9090
#prefer block bodies for methods
91-
csharp_style_expression_bodied_methods = false:suggestion
91+
csharp_style_expression_bodied_methods = true:suggestion
9292
#prefer expression-bodied members for properties when they will be a single line
9393
csharp_style_expression_bodied_properties = when_on_single_line:suggestion
9494

Src/MailMergeLib.Tests/EmailValidatorTest.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace MailMergeLib.Tests;
2727
public class EmailValidatorTest
2828
{
2929
static readonly string[] ValidAddresses =
30-
{
30+
[
3131
"\"Abc\\@def\"@example.com",
3232
"\"Fred Bloggs\"@example.com",
3333
"\"Joe\\\\Blow\"@example.com",
@@ -85,11 +85,11 @@ public class EmailValidatorTest
8585
"the-total-length@of-an-entire-address.cannot-be-longer-than-two-hundred-and-fifty-four-characters.and-this-address-is-254-characters-exactly.so-it-should-be-valid.and-im-going-to-add-some-more-words-here.to-increase-the-length-blah-blah-blah-blah-bla.org",
8686
"uncommon-tld@sld.mobi",
8787
"uncommon-tld@sld.museum",
88-
"uncommon-tld@sld.travel",
89-
};
88+
"uncommon-tld@sld.travel"
89+
];
9090

9191
static readonly string[] InvalidAddresses =
92-
{
92+
[
9393
"",
9494
"invalid",
9595
"invalid@",
@@ -134,15 +134,15 @@ public class EmailValidatorTest
134134
// examples of real (invalid) input from real users.
135135
"No longer available.",
136136
"Moved."
137-
};
137+
];
138138

139139
static readonly string[] ValidInternationalAddresses =
140-
{
140+
[
141141
"伊昭傑@郵件.商務", // Chinese
142142
"राम@मोहन.ईन्फो", // Hindi
143143
"юзер@екзампл.ком", // Ukranian
144-
"θσερ@εχαμπλε.ψομ", // Greek
145-
};
144+
"θσερ@εχαμπλε.ψομ" // Greek
145+
];
146146

147147
[Test]
148148
public void TestValidAddresses()
@@ -166,8 +166,5 @@ public void TestValidInternationalAddresses()
166166
}
167167

168168
[Test]
169-
public void TestThrowsExceptionIfNull()
170-
{
171-
Assert.Throws<ArgumentNullException>(() => EmailValidator.Validate(null!, true, true), "Null Address");
172-
}
169+
public void TestThrowsExceptionIfNull() => Assert.Throws<ArgumentNullException>(() => EmailValidator.Validate(null!, true, true), "Null Address");
173170
}

Src/MailMergeLib.Tests/FakeSmtpClient.cs

Lines changed: 17 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,19 @@ public override Task ConnectAsync(Stream stream, string host, int port = 0, Secu
6666
return Task.CompletedTask;
6767
}
6868

69-
public override Task DisconnectAsync(bool quit, CancellationToken cancellationToken = new CancellationToken())
70-
{
71-
return Task.CompletedTask;
72-
}
69+
public override Task DisconnectAsync(bool quit, CancellationToken cancellationToken = new CancellationToken()) => Task.CompletedTask;
7370

74-
public override Task NoOpAsync(CancellationToken cancellationToken = new CancellationToken())
75-
{
76-
return Task.CompletedTask;
77-
}
71+
public override Task NoOpAsync(CancellationToken cancellationToken = new CancellationToken()) => Task.CompletedTask;
7872

7973
public override Task<string> SendAsync(FormatOptions options, MimeMessage message,
80-
CancellationToken cancellationToken = new CancellationToken(), ITransferProgress? progress = null)
81-
{
82-
throw new NotImplementedException();
83-
}
74+
CancellationToken cancellationToken = new CancellationToken(), ITransferProgress? progress = null) =>
75+
throw new NotImplementedException();
8476

8577
public override Task<string> SendAsync(FormatOptions options, MimeMessage message, MailboxAddress sender, IEnumerable<MailboxAddress> recipients,
86-
CancellationToken cancellationToken = new CancellationToken(), ITransferProgress? progress = null)
87-
{
78+
CancellationToken cancellationToken = new CancellationToken(), ITransferProgress? progress = null) =>
8879
throw new NotImplementedException();
89-
}
9080

91-
protected override void OnNoRecipientsAccepted(MimeMessage message)
92-
{
93-
base.OnNoRecipientsAccepted(message);
94-
}
81+
protected override void OnNoRecipientsAccepted(MimeMessage message) => base.OnNoRecipientsAccepted(message);
9582

9683
public override void Authenticate(SaslMechanism mechanism, CancellationToken cancellationToken = new CancellationToken())
9784
{
@@ -132,35 +119,17 @@ public override void Connect(Stream stream, string host, int port = 0, SecureSoc
132119
return;
133120
}
134121

135-
protected override void OnSenderAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response)
136-
{
137-
base.OnSenderAccepted(message, mailbox, response);
138-
}
122+
protected override void OnSenderAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response) => base.OnSenderAccepted(message, mailbox, response);
139123

140-
protected override void OnSenderNotAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response)
141-
{
142-
base.OnSenderNotAccepted(message, mailbox, response);
143-
}
124+
protected override void OnSenderNotAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response) => base.OnSenderNotAccepted(message, mailbox, response);
144125

145-
protected override string GetEnvelopeId(MimeMessage message)
146-
{
147-
return base.GetEnvelopeId(message);
148-
}
126+
protected override string? GetEnvelopeId(MimeMessage message) => base.GetEnvelopeId(message);
149127

150-
protected override void OnRecipientAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response)
151-
{
152-
base.OnRecipientAccepted(message, mailbox, response);
153-
}
128+
protected override void OnRecipientAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response) => base.OnRecipientAccepted(message, mailbox, response);
154129

155-
protected override void OnRecipientNotAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response)
156-
{
157-
base.OnRecipientNotAccepted(message, mailbox, response);
158-
}
130+
protected override void OnRecipientNotAccepted(MimeMessage message, MailboxAddress mailbox, SmtpResponse response) => base.OnRecipientNotAccepted(message, mailbox, response);
159131

160-
protected override DeliveryStatusNotification? GetDeliveryStatusNotifications(MimeMessage message, MailboxAddress mailbox)
161-
{
162-
return base.GetDeliveryStatusNotifications(message, mailbox);
163-
}
132+
protected override DeliveryStatusNotification? GetDeliveryStatusNotifications(MimeMessage message, MailboxAddress mailbox) => base.GetDeliveryStatusNotifications(message, mailbox);
164133

165134
public override string Send(FormatOptions options, MimeMessage message, CancellationToken cancellationToken = new CancellationToken(),
166135
ITransferProgress? progress = null)
@@ -216,23 +185,11 @@ public override Task<string> SendAsync(MimeMessage message, MailboxAddress sende
216185
return Task.FromResult(string.Empty);
217186
}
218187

219-
protected override void OnMessageSent(MessageSentEventArgs e)
220-
{
221-
base.OnMessageSent(e);
222-
}
188+
protected override void OnMessageSent(MessageSentEventArgs e) => base.OnMessageSent(e);
223189

224-
protected override void OnConnected(string host, int port, SecureSocketOptions options)
225-
{
226-
base.OnConnected(host, port, options);
227-
}
190+
protected override void OnConnected(string host, int port, SecureSocketOptions options) => base.OnConnected(host, port, options);
228191

229-
protected override void OnDisconnected(string host, int port, SecureSocketOptions options, bool requested)
230-
{
231-
base.OnDisconnected(host, port, options, requested);
232-
}
192+
protected override void OnDisconnected(string host, int port, SecureSocketOptions options, bool requested) => base.OnDisconnected(host, port, options, requested);
233193

234-
protected override void OnAuthenticated(string message)
235-
{
236-
base.OnAuthenticated(message);
237-
}
238-
}
194+
protected override void OnAuthenticated(string message) => base.OnAuthenticated(message);
195+
}

Src/MailMergeLib.Tests/FileMessageStore_Serialization.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ public class FileMessageStore_Serialization
1313
[Test]
1414
public void SerializeDeserialize()
1515
{
16-
var fms = new FileMessageStore(new[] { TestFileFolders.FilesAbsPath }, new[] { "Msg*.xml" }, Encoding.UTF8);
16+
var fms = new FileMessageStore([TestFileFolders.FilesAbsPath], ["Msg*.xml"], Encoding.UTF8);
1717
Assert.That(FileMessageStore.Deserialize(fms.Serialize()), Is.EqualTo(fms));
1818
}
1919

2020
[Test]
2121
public void GetMessageInfosFromFiles()
2222
{
23-
var fms = new FileMessageStore(new[] { TestFileFolders.FilesAbsPath }, new[] { "Msg*.xml" }, Encoding.UTF8);
23+
var fms = new FileMessageStore([TestFileFolders.FilesAbsPath], ["Msg*.xml"], Encoding.UTF8);
2424
var messageInfos = fms.ScanForMessages().ToList();
2525

2626
Assert.That(messageInfos, Has.Count.EqualTo(2));
@@ -35,18 +35,18 @@ public void GetMessageInfosFromFiles()
3535
[Test]
3636
public void NoMessageFilesFound()
3737
{
38-
var fms = new FileMessageStore(new[] { TestFileFolders.FilesAbsPath }, new[] { Guid.NewGuid().ToString("N")}, Encoding.UTF8);
38+
var fms = new FileMessageStore([TestFileFolders.FilesAbsPath], [Guid.NewGuid().ToString("N")], Encoding.UTF8);
3939
var messageInfos = fms.ScanForMessages().ToList();
4040
Assert.That(messageInfos.Count, Is.EqualTo(0));
4141

42-
fms.SearchFolders = new[] { TestFileFolders.FilesAbsPath + Guid.NewGuid().ToString("N")};
42+
fms.SearchFolders = [TestFileFolders.FilesAbsPath + Guid.NewGuid().ToString("N")];
4343
Assert.Throws<DirectoryNotFoundException>(() => messageInfos = fms.ScanForMessages().ToList());
4444
}
4545

4646
[Test]
4747
public void FileSerialization()
4848
{
49-
var fms = new FileMessageStore(new[] { TestFileFolders.FilesAbsPath }, new[] { Guid.NewGuid().ToString("N") }, Encoding.UTF8);
49+
var fms = new FileMessageStore([TestFileFolders.FilesAbsPath], [Guid.NewGuid().ToString("N")], Encoding.UTF8);
5050
var tempFilename = Path.GetTempFileName();
5151
fms.Serialize(tempFilename, Encoding.UTF8);
5252
Assert.That(fms.Equals(FileMessageStore.Deserialize(tempFilename, Encoding.UTF8)), Is.True);
@@ -56,7 +56,7 @@ public void FileSerialization()
5656
[Test]
5757
public void StreamSerialization()
5858
{
59-
var fms = new FileMessageStore(new[] { TestFileFolders.FilesAbsPath }, new[] { Guid.NewGuid().ToString("N") }, Encoding.UTF8);
59+
var fms = new FileMessageStore([TestFileFolders.FilesAbsPath], [Guid.NewGuid().ToString("N")], Encoding.UTF8);
6060
var stream = new MemoryStream();
6161
fms.Serialize(stream, Encoding.UTF8);
6262
stream.Position = 0;

Src/MailMergeLib.Tests/Helper.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ internal class Helper
1818
/// That's why we need reliable way to find the assembly location, which is the base for relativ data folders.
1919
/// </remarks>
2020
/// <returns></returns>
21-
public static string GetCodeBaseDirectory()
22-
{
23-
return Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().Location).LocalPath)!;
24-
}
21+
public static string GetCodeBaseDirectory() => Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().Location).LocalPath)!;
2522

2623
internal static int Compare(Stream? a, Stream? b)
2724
{

Src/MailMergeLib.Tests/MailMergeLib.Tests.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@
3333
<ItemGroup>
3434
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
3535
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
36-
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="9.0.9" />
37-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
36+
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="10.0.10" />
37+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
3838
<PackageReference Include="netDumbster" Version="3.1.1" />
39-
<PackageReference Include="NUnit" Version="4.4.0" />
40-
<PackageReference Include="NUnit.Analyzers" Version="4.10.0">
39+
<PackageReference Include="NUnit" Version="4.6.1" />
40+
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
4141
<PrivateAssets>all</PrivateAssets>
4242
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4343
</PackageReference>
44-
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />
44+
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
4545
<PackageReference Include="System.ValueTuple" Version="4.6.2" />
4646
</ItemGroup>
4747

Src/MailMergeLib.Tests/MessageFactory.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ public static MailMergeMessage GetHtmlMailWithInlineAndOtherAttachments()
2828
// File.ReadAllText will include \r besides \n, while the internal C# representation is only \n
2929
HtmlText = string.Join("\n", File.ReadAllLines(Path.Combine(TestFileFolders.FilesAbsPath, HtmlTextFile))), // contains image (<img src="..." />) which must be "inline-attached"
3030
PlainText = string.Join("\n", File.ReadAllLines(Path.Combine(TestFileFolders.FilesAbsPath, PlainTextFile))),
31-
Templates = { new Template("Salutation", new Parts { new Part(PartType.Plain, "Hi", "Hi {FirstName}"), new Part(PartType.Plain, "Dear", "Dear {FirstName}"), new Part(PartType.Plain, "Formal", "Dear Sir or Madam") }, "Hi")},
31+
Templates = { new Template("Salutation",
32+
[
33+
new Part(PartType.Plain, "Hi", "Hi {FirstName}"), new Part(PartType.Plain, "Dear", "Dear {FirstName}"),
34+
new Part(PartType.Plain, "Formal", "Dear Sir or Madam")
35+
], "Hi")},
3236
Subject = Subject,
3337
Config = { FileBaseDirectory = TestFileFolders.FilesAbsPath, Organization = "MailMergeLib Inc.", CharacterEncoding = Encoding.UTF8, Priority = MessagePriority.Urgent }
3438
};
@@ -86,7 +90,7 @@ public static MailMergeMessage GetMessageWithAllPropertiesSet()
8690
mmm.FileAttachments.Add(new FileAttachment(Path.GetFullPath(Path.Combine(TestFileFolders.FilesAbsPath, PdfFile)), "information.pdf"));
8791
mmm.StringAttachments.Add(new StringAttachment("some content", "content.txt"));
8892
mmm.Headers.Add(HeaderId.Comments, "some comments for header");
89-
mmm.Config = new MailMergeLib.MessageConfig()
93+
mmm.Config = new MessageConfig()
9094
{
9195
FileBaseDirectory = TestFileFolders.FilesAbsPath,
9296
CharacterEncoding = Encoding.UTF32,
@@ -122,15 +126,14 @@ public static MailMergeMessage GetHtmlAndPlainMessage_WithTemplates(out Dictiona
122126
Templates =
123127
{
124128
new Template("Salutation",
125-
new Parts
126-
{
127-
new Part(PartType.Plain, "Hi", "Hi {FirstName}"),
128-
new Part(PartType.Html, "Hi", "Hi <b>{FirstName}</b><br>"),
129-
new Part(PartType.Plain, "Dear", "Dear {FirstName}"),
130-
new Part(PartType.Html, "Dear", "Dear <b>{FirstName}</b><br>"),
131-
new Part(PartType.Plain, "Formal", "Dear Sir or Madam"),
132-
new Part(PartType.Html, "Formal", "<b>Dear Sir or Madam</b><br>"),
133-
}, "Formal")
129+
[
130+
new Part(PartType.Plain, "Hi", "Hi {FirstName}"),
131+
new Part(PartType.Html, "Hi", "Hi <b>{FirstName}</b><br>"),
132+
new Part(PartType.Plain, "Dear", "Dear {FirstName}"),
133+
new Part(PartType.Html, "Dear", "Dear <b>{FirstName}</b><br>"),
134+
new Part(PartType.Plain, "Formal", "Dear Sir or Madam"),
135+
new Part(PartType.Html, "Formal", "<b>Dear Sir or Madam</b><br>")
136+
], "Formal")
134137
},
135138
Subject = "Message to {FirstName}",
136139
Config =
@@ -163,4 +166,4 @@ public static MailMergeMessage GetHtmlMessageForHtmlConverter()
163166

164167
return mmm;
165168
}
166-
}
169+
}

0 commit comments

Comments
 (0)