diff --git a/AMWD.Common.AspNetCore/Extensions/ApplicationBuilderExtensions.cs b/AMWD.Common.AspNetCore/Extensions/ApplicationBuilderExtensions.cs
index 108bfb1..293d7b1 100644
--- a/AMWD.Common.AspNetCore/Extensions/ApplicationBuilderExtensions.cs
+++ b/AMWD.Common.AspNetCore/Extensions/ApplicationBuilderExtensions.cs
@@ -16,26 +16,32 @@ namespace Microsoft.AspNetCore.Builder
/// Adds settings to run behind a reverse proxy (e.g. NginX).
///
///
- /// A base path (e.g. running in a sub-directory /app) for the application is defined via ASPNETCORE_APPL_PATH environment variable.
+ /// A base path (e.g. running in a sub-directory /app) for the application can be defined via ASPNETCORE_APPL_PATH environment variable.
///
///
/// Additionally you can specify the proxy server by using or a when there are multiple proxy servers.
///
- /// When no oder is set, the default subnets are configured:
- /// - 127.0.0.0/8
- /// - 10.0.0.0/8
- /// - 172.16.0.0/12
- /// - 192.168.0.0/16
+ /// When neither nor is set, the default subnets are configured:
+ ///
+ /// - 127.0.0.0/8
+ /// - ::1/128
///
- /// - ::1/128
- /// - fd00::/8
+ /// - 10.0.0.0/8
+ /// - 172.16.0.0/12
+ /// - 192.168.0.0/16
+ /// - fd00::/8
+ ///
///
/// The application builder.
/// The where proxy requests are received from (optional).
/// The where proxy requests are received from (optional).
- public static IApplicationBuilder UseProxyHosting(this IApplicationBuilder app, IPNetwork network = null, IPAddress address = null)
+ /// A custom base path (optional, ASPNETCORE_APPL_PATH is prefererred).
+ public static IApplicationBuilder UseProxyHosting(this IApplicationBuilder app, IPNetwork network = null, IPAddress address = null, string basePath = null)
{
string path = Environment.GetEnvironmentVariable("ASPNETCORE_APPL_PATH");
+ if (string.IsNullOrWhiteSpace(path))
+ path = basePath;
+
if (!string.IsNullOrWhiteSpace(path))
app.UsePathBase(new PathString(path));
@@ -46,15 +52,17 @@ namespace Microsoft.AspNetCore.Builder
if (network == null && address == null)
{
// localhost
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("127.0.0.0"), 8));
- options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("::1"), 128));
+ options.KnownNetworks.Add(new IPNetwork(IPAddress.Loopback, 8));
+ options.KnownNetworks.Add(new IPNetwork(IPAddress.IPv6Loopback, 128));
// private IPv4 networks
+ // see https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("172.16.0.0"), 12));
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("192.168.0.0"), 16));
// private IPv6 networks
+ // see https://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("fd00::"), 8));
}
diff --git a/AMWD.Common/AMWD.Common.csproj b/AMWD.Common/AMWD.Common.csproj
index d28ffb4..332ec50 100644
--- a/AMWD.Common/AMWD.Common.csproj
+++ b/AMWD.Common/AMWD.Common.csproj
@@ -34,10 +34,4 @@
-
-
- <_Parameter1>UnitTests
-
-
-
diff --git a/AMWD.Common/Extensions/CollectionExtensions.cs b/AMWD.Common/Extensions/CollectionExtensions.cs
new file mode 100644
index 0000000..2d0fb46
--- /dev/null
+++ b/AMWD.Common/Extensions/CollectionExtensions.cs
@@ -0,0 +1,55 @@
+namespace System.Collections.Generic
+{
+ ///
+ /// Provides extension methods for generic implementations.
+ ///
+ public static class CollectionExtensions
+ {
+ ///
+ /// Add the to the if it is not .
+ ///
+ /// The type of the elements in the collection
+ /// The collection.
+ /// The item to add if not .
+ public static void AddIfNotNull(this ICollection collection, T item)
+ {
+#if NET8_0_OR_GREATER
+ ArgumentNullException.ThrowIfNull(collection);
+#else
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+#endif
+
+ if (item == null)
+ return;
+
+ collection.Add(item);
+ }
+
+ ///
+ /// Adds a functionallity to .
+ ///
+ /// The type of the elements in the collection.
+ /// The collection.
+ /// The items to add.
+ public static void AddRange(this ICollection collection, IEnumerable items)
+ {
+#if NET8_0_OR_GREATER
+ ArgumentNullException.ThrowIfNull(collection);
+ ArgumentNullException.ThrowIfNull(items);
+#else
+ if (collection == null)
+ throw new ArgumentNullException(nameof(collection));
+
+ if (items == null)
+ throw new ArgumentNullException(nameof(items));
+#endif
+
+ if (collection == items)
+ return;
+
+ foreach (var item in items)
+ collection.Add(item);
+ }
+ }
+}
diff --git a/AMWD.Common/Extensions/DateTimeExtensions.cs b/AMWD.Common/Extensions/DateTimeExtensions.cs
index becc9f1..29338bc 100644
--- a/AMWD.Common/Extensions/DateTimeExtensions.cs
+++ b/AMWD.Common/Extensions/DateTimeExtensions.cs
@@ -61,20 +61,27 @@ namespace System
public static TimeSpan GetAlignedIntervalLocal(this TimeSpan timeSpan, TimeSpan offset = default)
=> timeSpan.GetAlignedInterval(DateTime.Now, offset);
- internal static TimeSpan GetAlignedInterval(this TimeSpan timeSpan, DateTime now, TimeSpan offset = default)
+ ///
+ /// Aligns the to the specified time.
+ ///
+ /// The timespan to align.
+ /// A timestamp to align with.
+ /// A specific offset to the timespan.
+ /// The timespan until the aligned time.
+ public static TimeSpan GetAlignedInterval(this TimeSpan timeSpan, DateTime now, TimeSpan offset = default)
{
- var nowWithOffset = new DateTimeOffset(now);
+ var dtOffsetNow = new DateTimeOffset(now);
- var nextTime = new DateTime(nowWithOffset.Ticks / timeSpan.Ticks * timeSpan.Ticks, now.Kind).Add(offset);
- var nextTimeWithOffset = new DateTimeOffset(nextTime);
+ var nextTime = new DateTime(dtOffsetNow.Ticks / timeSpan.Ticks * timeSpan.Ticks, now.Kind).Add(offset);
+ var dtOffsetNext = new DateTimeOffset(nextTime);
- if (nextTimeWithOffset <= nowWithOffset)
- nextTimeWithOffset = nextTimeWithOffset.Add(timeSpan);
+ if (dtOffsetNext <= dtOffsetNow)
+ dtOffsetNext = dtOffsetNext.Add(timeSpan);
if (now.Kind == DateTimeKind.Local)
- return nextTimeWithOffset.LocalDateTime - nowWithOffset.LocalDateTime;
+ return dtOffsetNext.LocalDateTime - dtOffsetNow.LocalDateTime;
- return nextTimeWithOffset - nowWithOffset;
+ return dtOffsetNext - dtOffsetNow;
}
#endregion Aligned Interval
diff --git a/AMWD.Common/InternalsVisibleTo.cs b/AMWD.Common/InternalsVisibleTo.cs
new file mode 100644
index 0000000..cd080bc
--- /dev/null
+++ b/AMWD.Common/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("UnitTests")]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb6784f..1b43cb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,21 +12,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [AMWD.Common](https://git.am-wd.de/AM.WD/common/compare/v2.0.1...HEAD)
- [AMWD.Common.AspNetCore](https://git.am-wd.de/AM.WD/common/compare/asp/v3.0.0...HEAD)
- [AMWD.Common.EntityFrameworkCore](https://git.am-wd.de/AM.WD/common/compare/efc/v3.0.0...HEAD)
+- [AMWD.Common.MessagePack](https://git.am-wd.de/AM.WD/common/compare/main...HEAD)
- [AMWD.Common.Test](https://git.am-wd.de/AM.WD/common/compare/test/v2.1.1...HEAD)
### Added
- `ArReader` and `ArWriter` for Unix archives
- `TarReader` and `TarWriter` for TAR archives
+- `.AddRange()` for collections
+- `.AddIfNotNull()` for collections
- `VersionStringComparer` to compare version strings (SemVer)
### Changed
- Optimized for C# 12
-- `IPNetwork` is used from (as `Microsoft.AspNetCore.HttpOverrides` is taged as "out of support"):
+- `IPNetwork` is used from (as `Microsoft.AspNetCore.HttpOverrides` is tagged as "out of support"):
- .NET Standard 2.0 and .NET 6.0: `Microsoft.AspNetCore.HttpOverrides.IPNetwork`
- .NET 8.0: `System.Net.IPNetwork`
-- Moved `MessagePack` extensions formatter extensions to own package `AMWD.Common.MessagePack`
+- Moved `MessagePack` formatter extensions to own package `AMWD.Common.MessagePack`
+- `GetAlignedInterval()` (without Local/Utc) is now public accessible
### Removed
diff --git a/README.md b/README.md
index a82d58e..b8c7d1f 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ To save time, they are all packed to NuGet packages.
| AMWD.Common |  |
| AMWD.Common.AspNetCore |  |
| AMWD.Common.EntityFrameworkCore |  |
+| AMWD.Common.MessagePack |  |
| AMWD.Common.Test |  |
| CI / CD |   |
diff --git a/UnitTests/Common/Extensions/CollectionExtensionsTests.cs b/UnitTests/Common/Extensions/CollectionExtensionsTests.cs
new file mode 100644
index 0000000..19762c3
--- /dev/null
+++ b/UnitTests/Common/Extensions/CollectionExtensionsTests.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTests.Common.Extensions
+{
+ [TestClass]
+ public class CollectionExtensionsTests
+ {
+ [TestMethod]
+ public void ShouldAddItem()
+ {
+ // Arrange
+ var item = new TestItem { Number = 10, Text = "Ten" };
+ ICollection list = new List
+ {
+ new() {
+ Number = 1,
+ Text = "One"
+ }
+ };
+
+ // Act
+ list.AddIfNotNull(item);
+
+ // Assert
+ Assert.AreEqual(2, list.Count);
+ }
+
+ [TestMethod]
+ public void ShouldNotAddItem()
+ {
+ // Arrange
+ TestItem item = null;
+ ICollection list = new List
+ {
+ new() {
+ Number = 1,
+ Text = "One"
+ }
+ };
+
+ // Act
+ list.AddIfNotNull(item);
+
+ // Assert
+ Assert.AreEqual(1, list.Count);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentNullException))]
+ public void ShouldThrowArgumentNullExceptionForNullList()
+ {
+ // Arrange
+ var item = new TestItem { Number = 10, Text = "Ten" };
+ ICollection list = null;
+
+ // Act
+ list.AddIfNotNull(item);
+
+ // Assert - ArgumentNullException
+ }
+
+ [TestMethod]
+ public void ShouldAddRange()
+ {
+ // Arrange
+ ICollection items = new List
+ {
+ new() { Number = 10, Text = "Ten" },
+ new() { Number = 11, Text = "Eleven" },
+ };
+ ICollection list = new List
+ {
+ new() { Number = 1, Text = "One" },
+ };
+
+ // Act
+ list.AddRange(items);
+
+ // Assert
+ Assert.AreEqual(3, list.Count);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentNullException))]
+ public void ShouldThrowArgumentNullExceptionForList()
+ {
+ // Arrange
+ ICollection items = new List
+ {
+ new() { Number = 10, Text = "Ten" },
+ new() { Number = 11, Text = "Eleven" },
+ };
+ ICollection list = null;
+
+ // Act
+ list.AddRange(items);
+
+ // Assert - ArgumentNullException
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentNullException))]
+ public void ShouldThrowArgumentNullExceptionForItems()
+ {
+ // Arrange
+ ICollection items = null;
+ ICollection list = new List
+ {
+ new() { Number = 1, Text = "One" },
+ };
+
+ // Act
+ list.AddRange(items);
+
+ // Assert - ArgumentNullException
+ }
+
+ [TestMethod]
+ public void ShouldNotAddRange()
+ {
+ // Arrange
+ ICollection list = new List
+ {
+ new() { Number = 1, Text = "One" },
+ };
+
+ // Act
+ list.AddRange(list);
+
+ // Assert
+ Assert.AreEqual(1, list.Count);
+ }
+
+ private class TestItem
+ {
+ public int Number { get; set; }
+
+ public string Text { get; set; }
+ }
+ }
+}