Added Collection Extensions, Updated DateTimeExtensions
This commit is contained in:
@@ -16,26 +16,32 @@ namespace Microsoft.AspNetCore.Builder
|
||||
/// Adds settings to run behind a reverse proxy (e.g. NginX).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A base path (e.g. running in a sub-directory /app) for the application is defined via <c>ASPNETCORE_APPL_PATH</c> environment variable.
|
||||
/// A base path (e.g. running in a sub-directory /app) for the application can be defined via <c>ASPNETCORE_APPL_PATH</c> environment variable.
|
||||
/// <br/>
|
||||
/// <br/>
|
||||
/// Additionally you can specify the proxy server by using <paramref name="address"/> or a <paramref name="network"/> when there are multiple proxy servers.
|
||||
/// <br/>
|
||||
/// When no <paramref name="address"/> oder <paramref name="network"/> is set, the default subnets are configured:<br/>
|
||||
/// - <c>127.0.0.0/8</c><br/>
|
||||
/// - <c>10.0.0.0/8</c><br/>
|
||||
/// - <c>172.16.0.0/12</c><br/>
|
||||
/// - <c>192.168.0.0/16</c><br/>
|
||||
/// When neither <paramref name="address"/> nor <paramref name="network"/> is set, the default subnets are configured:
|
||||
/// <list type="bullet">
|
||||
/// <item><c>127.0.0.0/8</c></item>
|
||||
/// <item><c>::1/128</c></item>
|
||||
///
|
||||
/// - <c>::1/128</c><br/>
|
||||
/// - <c>fd00::/8</c>
|
||||
/// <item><c>10.0.0.0/8</c></item>
|
||||
/// <item><c>172.16.0.0/12</c></item>
|
||||
/// <item><c>192.168.0.0/16</c></item>
|
||||
/// <item><c>fd00::/8</c></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <param name="app">The application builder.</param>
|
||||
/// <param name="network">The <see cref="IPNetwork"/> where proxy requests are received from (optional).</param>
|
||||
/// <param name="address">The <see cref="IPAddress"/> where proxy requests are received from (optional).</param>
|
||||
public static IApplicationBuilder UseProxyHosting(this IApplicationBuilder app, IPNetwork network = null, IPAddress address = null)
|
||||
/// <param name="basePath">A custom base path (optional, <c>ASPNETCORE_APPL_PATH</c> is prefererred).</param>
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,4 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
||||
<_Parameter1>UnitTests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
55
AMWD.Common/Extensions/CollectionExtensions.cs
Normal file
55
AMWD.Common/Extensions/CollectionExtensions.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for generic <see cref="ICollection{T}"/> implementations.
|
||||
/// </summary>
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Add the <paramref name="item"/> to the <paramref name="collection"/> if it is not <see langword="null"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the elements in the collection</typeparam>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="item">The item to add if not <see langword="null"/>.</param>
|
||||
public static void AddIfNotNull<T>(this ICollection<T> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AddRange{T}(ICollection{T}, IEnumerable{T})"/> functionallity to <see cref="ICollection{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the elements in the collection.</typeparam>
|
||||
/// <param name="collection">The collection.</param>
|
||||
/// <param name="items">The items to add.</param>
|
||||
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
/// <summary>
|
||||
/// Aligns the <see cref="TimeSpan"/> to the specified time.
|
||||
/// </summary>
|
||||
/// <param name="timeSpan">The timespan to align.</param>
|
||||
/// <param name="now">A timestamp to align with.</param>
|
||||
/// <param name="offset">A specific offset to the timespan.</param>
|
||||
/// <returns>The timespan until the aligned time.</returns>
|
||||
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
|
||||
|
||||
3
AMWD.Common/InternalsVisibleTo.cs
Normal file
3
AMWD.Common/InternalsVisibleTo.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("UnitTests")]
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |   |
|
||||
|
||||
|
||||
143
UnitTests/Common/Extensions/CollectionExtensionsTests.cs
Normal file
143
UnitTests/Common/Extensions/CollectionExtensionsTests.cs
Normal file
@@ -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<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() {
|
||||
Number = 1,
|
||||
Text = "One"
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddIfNotNull(item);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(2, list.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddItem()
|
||||
{
|
||||
// Arrange
|
||||
TestItem item = null;
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
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<TestItem> list = null;
|
||||
|
||||
// Act
|
||||
list.AddIfNotNull(item);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldAddRange()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = new List<TestItem>
|
||||
{
|
||||
new() { Number = 10, Text = "Ten" },
|
||||
new() { Number = 11, Text = "Eleven" },
|
||||
};
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() { Number = 1, Text = "One" },
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(3, list.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowArgumentNullExceptionForList()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = new List<TestItem>
|
||||
{
|
||||
new() { Number = 10, Text = "Ten" },
|
||||
new() { Number = 11, Text = "Eleven" },
|
||||
};
|
||||
ICollection<TestItem> list = null;
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void ShouldThrowArgumentNullExceptionForItems()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> items = null;
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
new() { Number = 1, Text = "One" },
|
||||
};
|
||||
|
||||
// Act
|
||||
list.AddRange(items);
|
||||
|
||||
// Assert - ArgumentNullException
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShouldNotAddRange()
|
||||
{
|
||||
// Arrange
|
||||
ICollection<TestItem> list = new List<TestItem>
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user