1
0

Updated to C# 12

This commit is contained in:
2024-01-14 13:10:33 +01:00
parent 9cd1344266
commit 27cd54fb30
51 changed files with 637 additions and 379 deletions

View File

@@ -28,7 +28,9 @@ build-debug:
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/ - mv ./AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/ - mv ./AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/ - mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/ - mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/ - mv ./AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/ - mv ./AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/
artifacts: artifacts:
@@ -72,7 +74,9 @@ build-release:
- mv ./AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/ - mv ./AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/ - mv ./AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/ - mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/ - mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/ - mv ./AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/ - mv ./AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/
artifacts: artifacts:
@@ -136,6 +140,19 @@ deploy-entityframework:
- if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/ - if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/
script: script:
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.EntityFrameworkCore.*.nupkg - dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.EntityFrameworkCore.*.nupkg
deploy-messagepack:
stage: deploy
dependencies:
- build-release
- test-release
tags:
- docker
- lnx
rules:
- if: $CI_COMMIT_TAG =~ /^msgpack\/v[0-9.]+/
script:
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.MessagePack.*.nupkg
deploy-test: deploy-test:
stage: deploy stage: deploy

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks> <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<LangVersion>10.0</LangVersion> <LangVersion>12.0</LangVersion>
<NrtTagMatch>asp/v[0-9]*</NrtTagMatch> <NrtTagMatch>asp/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.AspNetCore</AssemblyName> <AssemblyName>AMWD.Common.AspNetCore</AssemblyName>

View File

@@ -12,12 +12,12 @@ namespace Microsoft.AspNetCore.Http
public static class HttpContextExtensions public static class HttpContextExtensions
{ {
// Search these additional headers for a remote client ip address. // Search these additional headers for a remote client ip address.
private static readonly string[] _defaultIpHeaderNames = new[] private static readonly string[] _defaultIpHeaderNames =
{ [
"Cf-Connecting-Ip", // set by Cloudflare "Cf-Connecting-Ip", // set by Cloudflare
"X-Real-IP", // wide-spread alternative to X-Forwarded-For "X-Real-IP", // wide-spread alternative to X-Forwarded-For
"X-Forwarded-For", // commonly used on all known proxies "X-Forwarded-For", // commonly used on all known proxies
}; ];
/// <summary> /// <summary>
/// Retrieves the antiforgery token. /// Retrieves the antiforgery token.

View File

@@ -9,26 +9,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <summary> /// <summary>
/// Custom floating point ModelBinder as the team of Microsoft is not capable of fixing their <see href="https://github.com/dotnet/aspnetcore/issues/6566">issue</see> with other cultures than en-US. /// Custom floating point ModelBinder as the team of Microsoft is not capable of fixing their <see href="https://github.com/dotnet/aspnetcore/issues/6566">issue</see> with other cultures than en-US.
/// </summary> /// </summary>
/// <remarks>
/// Initializes a new instance of <see cref="InvariantFloatingPointModelBinder"/>.
/// </remarks>
/// <param name="supportedStyles">The <see cref="NumberStyles"/>.</param>
/// <param name="cultureInfo">The <see cref="CultureInfo"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class InvariantFloatingPointModelBinder : IModelBinder public class InvariantFloatingPointModelBinder(NumberStyles supportedStyles, CultureInfo cultureInfo, ILoggerFactory loggerFactory)
: IModelBinder
{ {
private readonly NumberStyles _supportedNumberStyles; private readonly NumberStyles _supportedNumberStyles = supportedStyles;
private readonly ILogger _logger; private readonly ILogger _logger = loggerFactory?.CreateLogger<InvariantFloatingPointModelBinder>();
private readonly CultureInfo _cultureInfo; private readonly CultureInfo _cultureInfo = cultureInfo ?? throw new ArgumentNullException(nameof(cultureInfo));
/// <summary>
/// Initializes a new instance of <see cref="InvariantFloatingPointModelBinder"/>.
/// </summary>
/// <param name="supportedStyles">The <see cref="NumberStyles"/>.</param>
/// <param name="cultureInfo">The <see cref="CultureInfo"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public InvariantFloatingPointModelBinder(NumberStyles supportedStyles, CultureInfo cultureInfo, ILoggerFactory loggerFactory)
{
_cultureInfo = cultureInfo ?? throw new ArgumentNullException(nameof(cultureInfo));
_supportedNumberStyles = supportedStyles;
_logger = loggerFactory?.CreateLogger<InvariantFloatingPointModelBinder>();
}
/// <inheritdoc /> /// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext) public Task BindModelAsync(ModelBindingContext bindingContext)

View File

@@ -13,49 +13,39 @@ using Microsoft.Extensions.Options;
namespace AMWD.Common.AspNetCore.Security.BasicAuthentication namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
{ {
#if NET8_0_OR_GREATER
/// <summary> /// <summary>
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication. /// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
/// </summary> /// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </remarks>
/// <param name="options" > The monitor for the options instance.</param>
/// <param name="logger">The <see cref="ILoggerFactory"/>.</param>
/// <param name="encoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="validator">An basic autentication validator implementation.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> public class BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, IBasicAuthenticationValidator validator)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
#else
/// <summary>
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </remarks>
/// <param name="options" > The monitor for the options instance.</param>
/// <param name="logger">The <see cref="ILoggerFactory"/>.</param>
/// <param name="encoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="clock">The <see cref="ISystemClock"/>.</param>
/// <param name="validator">An basic autentication validator implementation.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IBasicAuthenticationValidator validator)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder, clock)
#endif
{ {
private readonly ILogger _logger; private readonly ILogger _logger = logger.CreateLogger<BasicAuthenticationHandler>();
private readonly IBasicAuthenticationValidator _validator; private readonly IBasicAuthenticationValidator _validator = validator;
#if NET8_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </summary>
/// <param name="options">The monitor for the options instance.</param>
/// <param name="logger">The <see cref="ILoggerFactory"/>.</param>
/// <param name="encoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="validator">An basic autentication validator implementation.</param>
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, IBasicAuthenticationValidator validator)
: base(options, logger, encoder)
{
_logger = logger.CreateLogger<BasicAuthenticationHandler>();
_validator = validator;
}
#endif
#if NET6_0
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationHandler"/> class.
/// </summary>
/// <param name="options" > The monitor for the options instance.</param>
/// <param name="logger">The <see cref="ILoggerFactory"/>.</param>
/// <param name="encoder">The <see cref="UrlEncoder"/>.</param>
/// <param name="clock">The <see cref="ISystemClock"/>.</param>
/// <param name="validator">An basic autentication validator implementation.</param>
public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IBasicAuthenticationValidator validator)
: base(options, logger, encoder, clock)
{
_logger = logger.CreateLogger<BasicAuthenticationHandler>();
_validator = validator;
}
#endif
/// <inheritdoc/> /// <inheritdoc/>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() protected override async Task<AuthenticateResult> HandleAuthenticateAsync()

View File

@@ -11,21 +11,15 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
/// <summary> /// <summary>
/// Implements a basic authentication. /// Implements a basic authentication.
/// </summary> /// </summary>
public class BasicAuthenticationMiddleware /// <remarks>
/// Initializes a new instance of the <see cref="BasicAuthenticationMiddleware"/> class.
/// </remarks>
/// <param name="next">The following delegate in the process chain.</param>
/// <param name="validator">A basic authentication validator.</param>
public class BasicAuthenticationMiddleware(RequestDelegate next, IBasicAuthenticationValidator validator)
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next = next;
private readonly IBasicAuthenticationValidator _validator; private readonly IBasicAuthenticationValidator _validator = validator;
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationMiddleware"/> class.
/// </summary>
/// <param name="next">The following delegate in the process chain.</param>
/// <param name="validator">A basic authentication validator.</param>
public BasicAuthenticationMiddleware(RequestDelegate next, IBasicAuthenticationValidator validator)
{
_next = next;
_validator = validator;
}
/// <summary> /// <summary>
/// The delegate invokation. /// The delegate invokation.
@@ -35,15 +29,27 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
/// <returns>An awaitable task.</returns> /// <returns>An awaitable task.</returns>
public async Task InvokeAsync(HttpContext httpContext) public async Task InvokeAsync(HttpContext httpContext)
{ {
#if NET8_0_OR_GREATER
if (!httpContext.Request.Headers.TryGetValue("Authorization", out var authHeaderValue))
{
SetAuthenticateRequest(httpContext, _validator.Realm);
return;
}
#else
if (!httpContext.Request.Headers.ContainsKey("Authorization")) if (!httpContext.Request.Headers.ContainsKey("Authorization"))
{ {
SetAuthenticateRequest(httpContext, _validator.Realm); SetAuthenticateRequest(httpContext, _validator.Realm);
return; return;
} }
#endif
try try
{ {
#if NET8_0_OR_GREATER
var authHeader = AuthenticationHeaderValue.Parse(authHeaderValue);
#else
var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]); var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]);
#endif
byte[] decoded = Convert.FromBase64String(authHeader.Parameter); byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
string plain = Encoding.UTF8.GetString(decoded); string plain = Encoding.UTF8.GetString(decoded);
@@ -70,9 +76,9 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
private static void SetAuthenticateRequest(HttpContext httpContext, string realm) private static void SetAuthenticateRequest(HttpContext httpContext, string realm)
{ {
httpContext.Response.Headers["WWW-Authenticate"] = "Basic"; httpContext.Response.Headers.WWWAuthenticate = "Basic";
if (!string.IsNullOrWhiteSpace(realm)) if (!string.IsNullOrWhiteSpace(realm))
httpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Replace("\"", "")}\""; httpContext.Response.Headers.WWWAuthenticate = $"Basic realm=\"{realm.Replace("\"", "")}\"";
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized; httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
} }

View File

@@ -8,23 +8,16 @@ namespace AMWD.Common.AspNetCore.Security.PathProtection
/// <summary> /// <summary>
/// Implements a check to provide protected paths. /// Implements a check to provide protected paths.
/// </summary> /// </summary>
public class ProtectedPathMiddleware /// <remarks>
/// Initializes a new instance of the <see cref="ProtectedPathExtensions"/> class.
/// </remarks>
/// <param name="next">The following delegate in the process chain.</param>
/// <param name="options">The options to configure the middleware.</param>
public class ProtectedPathMiddleware(RequestDelegate next, ProtectedPathOptions options)
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next = next;
private readonly PathString _path; private readonly PathString _path = options.Path;
private readonly string _policyName; private readonly string _policyName = options.PolicyName;
/// <summary>
/// Initializes a new instance of the <see cref="ProtectedPathExtensions"/> class.
/// </summary>
/// <param name="next">The following delegate in the process chain.</param>
/// <param name="options">The options to configure the middleware.</param>
public ProtectedPathMiddleware(RequestDelegate next, ProtectedPathOptions options)
{
_next = next;
_path = options.Path;
_policyName = options.PolicyName;
}
/// <summary> /// <summary>
/// The delegate invokation. /// The delegate invokation.

View File

@@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
if (items.Any()) if (items.Any())
{ {
string classes = string.Join(" ", items.ToArray()); string classes = string.Join(" ", [.. items]);
output.Attributes.Add("class", classes); output.Attributes.Add("class", classes);
} }
} }

View File

@@ -13,24 +13,19 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary> /// <summary>
/// A tag helper to dynamically create integrity checks for linked sources. /// A tag helper to dynamically create integrity checks for linked sources.
/// </summary> /// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="IntegrityHashTagHelper"/> class.
/// </remarks>
/// <param name="env">The web host environment.</param>
/// <param name="configuration">The application configuration.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement("link")] [HtmlTargetElement("link")]
[HtmlTargetElement("script")] [HtmlTargetElement("script")]
public class IntegrityHashTagHelper : TagHelper public class IntegrityHashTagHelper(IWebHostEnvironment env, IConfiguration configuration)
: TagHelper
{ {
private readonly IWebHostEnvironment _env; private readonly IWebHostEnvironment _env = env;
private readonly string _hostUrl; private readonly string _hostUrl = configuration.GetValue("ASPNETCORE_APPL_URL", "http://localhost/");
/// <summary>
/// Initializes a new instance of the <see cref="IntegrityHashTagHelper"/> class.
/// </summary>
/// <param name="env">The web host environment.</param>
/// <param name="configuration">The application configuration.</param>
public IntegrityHashTagHelper(IWebHostEnvironment env, IConfiguration configuration)
{
_env = env;
_hostUrl = configuration.GetValue("ASPNETCORE_APPL_URL", "http://localhost/");
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the integrity should be calculated. /// Gets or sets a value indicating whether the integrity should be calculated.
@@ -118,7 +113,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
} }
string type; string type;
byte[] hashBytes = Array.Empty<byte>(); byte[] hashBytes = [];
switch (IntegrityStrength) switch (IntegrityStrength)
{ {
case 512: case 512:

View File

@@ -9,17 +9,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <summary> /// <summary>
/// Adds additional behavior to the modelbinding for numeric properties. /// Adds additional behavior to the modelbinding for numeric properties.
/// </summary> /// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="NumberInputTagHelper"/> class.
/// </remarks>
/// <param name="generator">The HTML generator.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement("input", Attributes = "asp-for")] [HtmlTargetElement("input", Attributes = "asp-for")]
public class NumberInputTagHelper : InputTagHelper public class NumberInputTagHelper(IHtmlGenerator generator)
: InputTagHelper(generator)
{ {
/// <summary>
/// Initializes a new instance of the <see cref="NumberInputTagHelper"/> class.
/// </summary>
/// <param name="generator">The HTML generator.</param>
public NumberInputTagHelper(IHtmlGenerator generator)
: base(generator)
{ }
/// <inheritdoc /> /// <inheritdoc />
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks> <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<LangVersion>10.0</LangVersion> <LangVersion>12.0</LangVersion>
<NrtTagMatch>efc/v[0-9]*</NrtTagMatch> <NrtTagMatch>efc/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.EntityFrameworkCore</AssemblyName> <AssemblyName>AMWD.Common.EntityFrameworkCore</AssemblyName>
@@ -22,17 +22,17 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.25" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.26" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.25" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.26" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,4 @@
using System.Runtime.Serialization; namespace System
namespace System
{ {
/// <summary> /// <summary>
/// A DatabaseProvider specific exception. /// A DatabaseProvider specific exception.
@@ -33,16 +31,16 @@ namespace System
: base(message, innerException) : base(message, innerException)
{ } { }
#if NET6_0 #if !NET8_0_OR_GREATER
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DatabaseProviderException"/> class with serialized data. /// Initializes a new instance of the <see cref="DatabaseProviderException"/> class with serialized data.
/// </summary> /// </summary>
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param> /// <param name="info">The <see cref="Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param> /// <param name="context">The <see cref="Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
/// <exception cref="ArgumentNullException">The info parameter is null.</exception> /// <exception cref="ArgumentNullException">The info parameter is null.</exception>
/// <exception cref="SerializationException">The class name is null or <see cref="Exception.HResult"/> is zero (0).</exception> /// <exception cref="Runtime.Serialization.SerializationException">The class name is null or <see cref="Exception.HResult"/> is zero (0).</exception>
protected DatabaseProviderException(SerializationInfo info, StreamingContext context) protected DatabaseProviderException(Runtime.Serialization.SerializationInfo info, Runtime.Serialization.StreamingContext context)
: base(info, context) : base(info, context)
{ } { }

View File

@@ -15,7 +15,11 @@ namespace Microsoft.EntityFrameworkCore
/// <summary> /// <summary>
/// Extensions for the <see cref="DatabaseFacade"/>. /// Extensions for the <see cref="DatabaseFacade"/>.
/// </summary> /// </summary>
#if NET8_0_OR_GREATER
public static partial class DatabaseFacadeExtensions
#else
public static class DatabaseFacadeExtensions public static class DatabaseFacadeExtensions
#endif
{ {
/// <summary> /// <summary>
/// Applies migration files to the database. /// Applies migration files to the database.
@@ -23,7 +27,7 @@ namespace Microsoft.EntityFrameworkCore
/// <param name="database">The database connection.</param> /// <param name="database">The database connection.</param>
/// <param name="optionsAction">An action to set additional options.</param> /// <param name="optionsAction">An action to set additional options.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>true on success, otherwise false or an exception is thrown.</returns> /// <returns><see langword="true"/> on success, otherwise false or an exception is thrown.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2208")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2208")]
public static async Task<bool> ApplyMigrationsAsync(this DatabaseFacade database, Action<DatabaseMigrationOptions> optionsAction, CancellationToken cancellationToken = default) public static async Task<bool> ApplyMigrationsAsync(this DatabaseFacade database, Action<DatabaseMigrationOptions> optionsAction, CancellationToken cancellationToken = default)
{ {
@@ -211,20 +215,20 @@ END;"
if (options.SourceAssembly == null) if (options.SourceAssembly == null)
{ {
availableMigrationFiles = Directory.GetFiles(options.Path) availableMigrationFiles = Directory.GetFiles(options.Path)
.Where(f => f.ToLower().StartsWith(options.Path.ToLower())) .Where(f => f.StartsWith(options.Path, StringComparison.OrdinalIgnoreCase))
.Where(f => f.ToLower().EndsWith(".sql")) .Where(f => f.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
} }
else else
{ {
availableMigrationFiles = options.SourceAssembly availableMigrationFiles = options.SourceAssembly
.GetManifestResourceNames() .GetManifestResourceNames()
.Where(f => f.ToLower().StartsWith(options.Path.ToLower())) .Where(f => f.StartsWith(options.Path, StringComparison.OrdinalIgnoreCase))
.Where(f => f.ToLower().EndsWith(".sql")) .Where(f => f.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
.ToList(); .ToList();
} }
if (!availableMigrationFiles.Any()) if (availableMigrationFiles.Count == 0)
return true; return true;
using var command = connection.CreateCommand(); using var command = connection.CreateCommand();
@@ -270,7 +274,11 @@ END;"
{ {
using var stream = options.SourceAssembly.GetManifestResourceStream(migrationFile); using var stream = options.SourceAssembly.GetManifestResourceStream(migrationFile);
using var sr = new StreamReader(stream); using var sr = new StreamReader(stream);
#if NET8_0_OR_GREATER
sqlScript = await sr.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
#else
sqlScript = await sr.ReadToEndAsync().ConfigureAwait(false); sqlScript = await sr.ReadToEndAsync().ConfigureAwait(false);
#endif
} }
if (string.IsNullOrWhiteSpace(sqlScript)) if (string.IsNullOrWhiteSpace(sqlScript))
@@ -316,7 +324,11 @@ END;"
{ {
int affectedRows = 0; int affectedRows = 0;
// Split script by a single slash in a line // Split script by a single slash in a line
#if NET8_0_OR_GREATER
string[] parts = FindSingleSlashInLine().Split(text);
#else
string[] parts = Regex.Split(text, @"\r?\n[ \t]*/[ \t]*\r?\n"); string[] parts = Regex.Split(text, @"\r?\n[ \t]*/[ \t]*\r?\n");
#endif
foreach (string part in parts) foreach (string part in parts)
{ {
// Make writable copy // Make writable copy
@@ -325,7 +337,11 @@ END;"
// Remove the trailing semicolon from commands where they're not supported // Remove the trailing semicolon from commands where they're not supported
// (Oracle doesn't like semicolons. To keep the semicolon, it must be directly // (Oracle doesn't like semicolons. To keep the semicolon, it must be directly
// preceeded by "end".) // preceeded by "end".)
pt = Regex.Replace(pt.TrimEnd(), @"(?<!end);$", "", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); #if NET8_0_OR_GREATER
pt = FindEndCommand().Replace(pt.TrimEnd(), "");
#else
pt = Regex.Replace(pt, @"(?<!end);$", "", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
#endif
// Execute all non-empty parts as individual commands // Execute all non-empty parts as individual commands
if (!string.IsNullOrWhiteSpace(pt)) if (!string.IsNullOrWhiteSpace(pt))
@@ -352,5 +368,13 @@ END;"
SQLServer = 5, SQLServer = 5,
InMemory = 6, InMemory = 6,
} }
#if NET8_0_OR_GREATER
[GeneratedRegex(@"\r?\n[ \t]*/[ \t]*\r?\n")]
private static partial Regex FindSingleSlashInLine();
[GeneratedRegex(@"(?<!end);$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
private static partial Regex FindEndCommand();
#endif
} }
} }

View File

@@ -33,11 +33,15 @@ namespace Microsoft.EntityFrameworkCore
/// <returns>The <see cref="DbContextOptionsBuilder"/> with applied settings.</returns> /// <returns>The <see cref="DbContextOptionsBuilder"/> with applied settings.</returns>
public static DbContextOptionsBuilder UseDatabaseProvider(this DbContextOptionsBuilder optionsBuilder, IConfiguration configuration, Action<DatabaseProviderOptions> optionsAction = null) public static DbContextOptionsBuilder UseDatabaseProvider(this DbContextOptionsBuilder optionsBuilder, IConfiguration configuration, Action<DatabaseProviderOptions> optionsAction = null)
{ {
#if NET8_0_OR_GREATER
ArgumentNullException.ThrowIfNull(optionsBuilder);
ArgumentNullException.ThrowIfNull(configuration);
#else
if (optionsBuilder == null) if (optionsBuilder == null)
throw new ArgumentNullException(nameof(optionsBuilder)); throw new ArgumentNullException(nameof(optionsBuilder));
if (configuration == null) if (configuration == null)
throw new ArgumentNullException(nameof(configuration)); throw new ArgumentNullException(nameof(configuration));
#endif
var options = new DatabaseProviderOptions(); var options = new DatabaseProviderOptions();
optionsAction?.Invoke(options); optionsAction?.Invoke(options);

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>msgpack/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.MessagePack</AssemblyName>
<RootNamespace>AMWD.Common.MessagePack</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>AMWD.Common.MessagePack</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library for MessagePack</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.140" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
</ItemGroup>
</Project>

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using AMWD.Common.Utilities; using AMWD.Common.MessagePack.Utilities;
namespace MessagePack.Formatters namespace MessagePack.Formatters
{ {
@@ -66,7 +66,7 @@ namespace MessagePack.Formatters
bytes.AddRange(buffer); bytes.AddRange(buffer);
} }
options.Resolver.GetFormatterWithVerify<byte[]>().Serialize(ref writer, bytes.ToArray(), options); options.Resolver.GetFormatterWithVerify<byte[]>().Serialize(ref writer, [.. bytes], options);
} }
} }
} }

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using AMWD.Common.Utilities; using AMWD.Common.MessagePack.Utilities;
namespace MessagePack.Formatters namespace MessagePack.Formatters
{ {
@@ -61,7 +61,7 @@ namespace MessagePack.Formatters
bytes.AddRange(buffer); bytes.AddRange(buffer);
} }
options.Resolver.GetFormatterWithVerify<byte[]>().Serialize(ref writer, bytes.ToArray(), options); options.Resolver.GetFormatterWithVerify<byte[]>().Serialize(ref writer, [.. bytes], options);
} }
} }
} }

View File

@@ -1,12 +1,15 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using AMWD.Common.Utilities; using AMWD.Common.MessagePack.Utilities;
using MessagePack; #if NET8_0_OR_GREATER
using MessagePack.Formatters; using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace AMWD.Common.Formatters namespace MessagePack.Formatters
{ {
/// <summary> /// <summary>
/// Serialization of an <see cref="IPNetwork"/> array to and from <see cref="MessagePack"/>. /// Serialization of an <see cref="IPNetwork"/> array to and from <see cref="MessagePack"/>.

View File

@@ -1,7 +1,12 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace MessagePack.Formatters namespace MessagePack.Formatters
{ {
@@ -15,7 +20,7 @@ namespace MessagePack.Formatters
public IPNetwork Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) public IPNetwork Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{ {
if (reader.IsNil) if (reader.IsNil)
return null; return default;
byte[] bytes = options.Resolver.GetFormatterWithVerify<byte[]>().Deserialize(ref reader, options); byte[] bytes = options.Resolver.GetFormatterWithVerify<byte[]>().Deserialize(ref reader, options);
return DeserializeInternal(bytes); return DeserializeInternal(bytes);
@@ -24,7 +29,7 @@ namespace MessagePack.Formatters
/// <inheritdoc/> /// <inheritdoc/>
public void Serialize(ref MessagePackWriter writer, IPNetwork value, MessagePackSerializerOptions options) public void Serialize(ref MessagePackWriter writer, IPNetwork value, MessagePackSerializerOptions options)
{ {
if (value == null) if (value == default)
{ {
writer.WriteNil(); writer.WriteNil();
return; return;
@@ -38,7 +43,11 @@ namespace MessagePack.Formatters
{ {
// IP network prefix has a maximum of 128 bit - therefore the length can be covered with a byte. // IP network prefix has a maximum of 128 bit - therefore the length can be covered with a byte.
byte prefixLength = (byte)network.PrefixLength; byte prefixLength = (byte)network.PrefixLength;
#if NET8_0_OR_GREATER
byte[] prefixBytes = network.BaseAddress.GetAddressBytes();
#else
byte[] prefixBytes = network.Prefix.GetAddressBytes(); byte[] prefixBytes = network.Prefix.GetAddressBytes();
#endif
byte[] bytes = new byte[prefixBytes.Length + 1]; byte[] bytes = new byte[prefixBytes.Length + 1];
bytes[0] = prefixLength; bytes[0] = prefixLength;

View File

@@ -1,8 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using AMWD.Common.Utilities; using AMWD.Common.MessagePack.Utilities;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace MessagePack.Formatters namespace MessagePack.Formatters
{ {

View File

@@ -0,0 +1,17 @@
using System;
namespace AMWD.Common.MessagePack.Utilities
{
/// <summary>
/// Provides some network utils.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static class NetworkHelper
{
public static void SwapBigEndian(byte[] array)
{
if (BitConverter.IsLittleEndian)
Array.Reverse(array);
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion> <LangVersion>12.0</LangVersion>
<NrtTagMatch>test/v[0-9]*</NrtTagMatch> <NrtTagMatch>test/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.Test</AssemblyName> <AssemblyName>AMWD.Common.Test</AssemblyName>
@@ -22,7 +22,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Moq" Version="4.20.69" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup> </ItemGroup>

View File

@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<LangVersion>10.0</LangVersion> <LangVersion>12.0</LangVersion>
<AssemblyName>AMWD.Common</AssemblyName> <AssemblyName>AMWD.Common</AssemblyName>
<RootNamespace>AMWD.Common</RootNamespace> <RootNamespace>AMWD.Common</RootNamespace>
@@ -21,10 +21,23 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.129" />
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" /> <PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>UnitTests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project> </Project>

View File

@@ -14,7 +14,7 @@ namespace AMWD.Common.Cli
private string[] _args; private string[] _args;
private List<Argument> _parsedArguments; private List<Argument> _parsedArguments;
private readonly List<Option> _options = new(); private readonly List<Option> _options = [];
#endregion Private data #endregion Private data
@@ -129,7 +129,7 @@ namespace AMWD.Common.Cli
{ {
args.Add(currentArg.ToString()); args.Add(currentArg.ToString());
} }
return args.ToArray(); return [.. args];
} }
/// <summary> /// <summary>
@@ -199,7 +199,7 @@ namespace AMWD.Common.Cli
} }
// Clear/reset data // Clear/reset data
_parsedArguments = new(); _parsedArguments = [];
foreach (var option in _options) foreach (var option in _options)
{ {
option.IsSet = false; option.IsSet = false;
@@ -223,7 +223,7 @@ namespace AMWD.Common.Cli
string optName = arg.Substring(arg.StartsWith("--") ? 2 : 1); string optName = arg.Substring(arg.StartsWith("--") ? 2 : 1);
// Split option value if separated with : or = instead of whitespace // Split option value if separated with : or = instead of whitespace
int separatorIndex = optName.IndexOfAny(new[] { ':', '=' }); int separatorIndex = optName.IndexOfAny([':', '=']);
string optValue = null; string optValue = null;
if (separatorIndex != -1) if (separatorIndex != -1)
{ {
@@ -288,7 +288,7 @@ namespace AMWD.Common.Cli
} }
else else
{ {
_parsedArguments.Add(new Argument(null, new[] { arg })); _parsedArguments.Add(new Argument(null, [arg]));
} }
} }
@@ -315,7 +315,7 @@ namespace AMWD.Common.Cli
if (_parsedArguments == null) if (_parsedArguments == null)
Parse(); Parse();
return _parsedArguments.ToArray(); return [.. _parsedArguments];
} }
} }

View File

@@ -8,21 +8,16 @@ namespace AMWD.Common.Cli
/// Walks through an <see cref="IEnumerable{T}"/> and allows retrieving additional items. /// Walks through an <see cref="IEnumerable{T}"/> and allows retrieving additional items.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
internal class EnumerableWalker<T> : IEnumerable<T> /// <remarks>
where T : class /// Initialises a new instance of the <see cref="EnumerableWalker{T}"/> class.
/// </remarks>
/// <param name="array">The array to walk though.</param>
internal class EnumerableWalker<T>(IEnumerable<T> array)
: IEnumerable<T> where T : class
{ {
private readonly IEnumerable<T> _array; private readonly IEnumerable<T> _array = array ?? throw new ArgumentNullException(nameof(array));
private IEnumerator<T> _enumerator; private IEnumerator<T> _enumerator;
/// <summary>
/// Initialises a new instance of the <see cref="EnumerableWalker{T}"/> class.
/// </summary>
/// <param name="array">The array to walk though.</param>
public EnumerableWalker(IEnumerable<T> array)
{
_array = array ?? throw new ArgumentNullException(nameof(array));
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() IEnumerator<T> IEnumerable<T>.GetEnumerator()
{ {
_enumerator = _array.GetEnumerator(); _enumerator = _array.GetEnumerator();

View File

@@ -16,7 +16,7 @@ namespace AMWD.Common.Cli
/// <param name="parameterCount">The number of additional parameters for this option.</param> /// <param name="parameterCount">The number of additional parameters for this option.</param>
internal Option(string name, int parameterCount) internal Option(string name, int parameterCount)
{ {
Names = new List<string>() { name }; Names = [name];
ParameterCount = parameterCount; ParameterCount = parameterCount;
} }

View File

@@ -13,18 +13,16 @@ namespace Newtonsoft.Json
/// <summary> /// <summary>
/// List of known types to use this converver. /// List of known types to use this converver.
/// </summary> /// </summary>
public static readonly Type[] KnownTypes = new[] public static readonly Type[] KnownTypes =
{ [
typeof(byte[]), typeof(byte[]),
typeof(List<byte>), typeof(List<byte>),
typeof(IEnumerable<byte>) typeof(IEnumerable<byte>)
}; ];
/// <inheritdoc/> /// <inheritdoc/>
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)
{ => KnownTypes.Contains(objectType);
return KnownTypes.Contains(objectType);
}
/// <inheritdoc/> /// <inheritdoc/>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

View File

@@ -14,19 +14,17 @@ namespace Newtonsoft.Json
/// <summary> /// <summary>
/// List of known types to use this converver. /// List of known types to use this converver.
/// </summary> /// </summary>
public static readonly Type[] KnownTypes = new[] public static readonly Type[] KnownTypes =
{ [
typeof(IPAddress), typeof(IPAddress),
typeof(IPAddress[]), typeof(IPAddress[]),
typeof(List<IPAddress>), typeof(List<IPAddress>),
typeof(IEnumerable<IPAddress>) typeof(IEnumerable<IPAddress>)
}; ];
/// <inheritdoc/> /// <inheritdoc/>
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)
{ => KnownTypes.Contains(objectType);
return KnownTypes.Contains(objectType);
}
/// <inheritdoc/> /// <inheritdoc/>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

View File

@@ -2,7 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace Newtonsoft.Json namespace Newtonsoft.Json
{ {
@@ -10,24 +15,22 @@ namespace Newtonsoft.Json
/// Converts an <see cref="IPNetwork"/> from and to JSON. /// Converts an <see cref="IPNetwork"/> from and to JSON.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class IPNetworkConverter : JsonConverter public class IpNetworkConverter : JsonConverter
{ {
/// <summary> /// <summary>
/// List of known types to use this converver. /// List of known types to use this converver.
/// </summary> /// </summary>
public static readonly Type[] KnownTypes = new[] public static readonly Type[] KnownTypes =
{ [
typeof(IPNetwork), typeof(IPNetwork),
typeof(IPNetwork[]), typeof(IPNetwork[]),
typeof(List<IPNetwork>), typeof(List<IPNetwork>),
typeof(IEnumerable<IPNetwork>) typeof(IEnumerable<IPNetwork>)
}; ];
/// <inheritdoc/> /// <inheritdoc/>
public override bool CanConvert(Type objectType) public override bool CanConvert(Type objectType)
{ => KnownTypes.Contains(objectType);
return KnownTypes.Contains(objectType);
}
/// <inheritdoc/> /// <inheritdoc/>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
@@ -40,7 +43,7 @@ namespace Newtonsoft.Json
if (typeof(IPNetwork) == objectType) if (typeof(IPNetwork) == objectType)
return Parse(str); return Parse(str);
var networks = str.Split(';').Select(s => Parse(s)); var networks = str.Split(';').Select(Parse);
if (typeof(IPNetwork[]) == objectType) if (typeof(IPNetwork[]) == objectType)
return networks.ToArray(); return networks.ToArray();
@@ -65,23 +68,27 @@ namespace Newtonsoft.Json
str = ToString(net); str = ToString(net);
if (value is IPNetwork[] netArray) if (value is IPNetwork[] netArray)
str = string.Join(";", netArray.Select(n => ToString(n))); str = string.Join(";", netArray.Select(ToString));
if (value is List<IPNetwork> netList) if (value is List<IPNetwork> netList)
str = string.Join(";", netList.Select(n => ToString(n))); str = string.Join(";", netList.Select(ToString));
if (value is IEnumerable<IPNetwork> netEnum) if (value is IEnumerable<IPNetwork> netEnum)
str = string.Join(";", netEnum.Select(n => ToString(n))); str = string.Join(";", netEnum.Select(ToString));
writer.WriteValue(str); writer.WriteValue(str);
} }
private string ToString(IPNetwork net) private static string ToString(IPNetwork net)
{ {
#if NET8_0_OR_GREATER
return $"{net.BaseAddress}/{net.PrefixLength}";
#else
return $"{net.Prefix}/{net.PrefixLength}"; return $"{net.Prefix}/{net.PrefixLength}";
#endif
} }
private IPNetwork Parse(string str) private static IPNetwork Parse(string str)
{ {
string[] parts = str.Split('/'); string[] parts = str.Split('/');
var prefix = IPAddress.Parse(parts.First()); var prefix = IPAddress.Parse(parts.First());

View File

@@ -1,7 +1,5 @@
using System.Text; using System.Text;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
namespace System namespace System
{ {
/// <summary> /// <summary>

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
namespace System namespace System
@@ -41,13 +40,5 @@ namespace System
/// <returns>The description or the string representation of the value.</returns> /// <returns>The description or the string representation of the value.</returns>
public static string GetDescription(this Enum value) public static string GetDescription(this Enum value)
=> value.GetAttribute<DescriptionAttribute>()?.Description ?? value.ToString(); => value.GetAttribute<DescriptionAttribute>()?.Description ?? value.ToString();
/// <summary>
/// Returns the name from <see cref="DisplayAttribute"/>.
/// </summary>
/// <param name="value">The enum value.</param>
/// <returns>The display name or the string representation of the value.</returns>
public static string GetDisplayName(this Enum value)
=> value.GetAttribute<DisplayAttribute>()?.Name ?? value.ToString();
} }
} }

View File

@@ -56,16 +56,11 @@
return new DisposableReadWriteLock(rwLock, LockMode.Write); return new DisposableReadWriteLock(rwLock, LockMode.Write);
} }
private struct DisposableReadWriteLock : IDisposable private struct DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
: IDisposable
{ {
private readonly ReaderWriterLockSlim _rwLock; private readonly ReaderWriterLockSlim _rwLock = rwLock;
private LockMode _lockMode; private LockMode _lockMode = lockMode;
public DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
{
_rwLock = rwLock;
_lockMode = lockMode;
}
public void Dispose() public void Dispose()
{ {

View File

@@ -40,7 +40,7 @@ namespace System.IO
} }
while (ch != eol); while (ch != eol);
return encoding.GetString(bytes.ToArray()).Trim(); return encoding.GetString([.. bytes]).Trim();
} }
/// <summary> /// <summary>
@@ -73,7 +73,7 @@ namespace System.IO
} }
while (ch != eol); while (ch != eol);
return encoding.GetString(bytes.ToArray()).Trim(); return encoding.GetString([.. bytes]).Trim();
} }
} }
} }

View File

@@ -15,7 +15,12 @@ namespace System
/// <summary> /// <summary>
/// String extensions. /// String extensions.
/// </summary> /// </summary>
#if NET8_0_OR_GREATER
public static partial class StringExtensions
#else
public static class StringExtensions public static class StringExtensions
#endif
{ {
/// <summary> /// <summary>
/// Converts a hex string into a byte array. /// Converts a hex string into a byte array.
@@ -32,8 +37,13 @@ namespace System
if (str.Length % 2 == 1) if (str.Length % 2 == 1)
yield break; yield break;
#if NET8_0_OR_GREATER
if (InvalidHexCharRegex().IsMatch(str))
yield break;
#else
if (Regex.IsMatch(str, "[^0-9a-fA-F]")) if (Regex.IsMatch(str, "[^0-9a-fA-F]"))
yield break; yield break;
#endif
for (int i = 0; i < str.Length; i += 2) for (int i = 0; i < str.Length; i += 2)
yield return Convert.ToByte(str.Substring(i, 2), 16); yield return Convert.ToByte(str.Substring(i, 2), 16);
@@ -179,14 +189,14 @@ namespace System
{ {
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0"); var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS"); var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS");
var resolveMethodInfo = dnsClientType.GetMethod("Resolve", new[] { typeof(string), recordTypeType, typeof(CancellationToken) }); var resolveMethodInfo = dnsClientType.GetMethod("Resolve", [typeof(string), recordTypeType, typeof(CancellationToken)]);
bool exists = false; bool exists = false;
foreach (var nameserver in nameservers) foreach (var nameserver in nameservers)
{ {
object dnsClient = Activator.CreateInstance(dnsClientType, new object[] { nameserver }); object dnsClient = Activator.CreateInstance(dnsClientType, [nameserver]);
var waitTask = Task.Run(async () => await resolveMethodInfo.InvokeAsync<object>(dnsClient, new object[] { mailAddress.Host, 15, CancellationToken.None })); // 15 = MX Record var waitTask = Task.Run(async () => await resolveMethodInfo.InvokeAsync<object>(dnsClient, [mailAddress.Host, 15, CancellationToken.None])); // 15 = MX Record
waitTask.Wait(); waitTask.Wait();
object response = waitTask.Result; object response = waitTask.Result;
@@ -232,5 +242,10 @@ namespace System
/// <returns></returns> /// <returns></returns>
public static StringBuilder AppendLine(this StringBuilder sb, string value, string newLine) public static StringBuilder AppendLine(this StringBuilder sb, string value, string newLine)
=> sb.Append(value).Append(newLine); => sb.Append(value).Append(newLine);
#if NET8_0_OR_GREATER
[GeneratedRegex("[^0-9a-fA-F]")]
private static partial Regex InvalidHexCharRegex();
#endif
} }
} }

View File

@@ -6,8 +6,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
namespace AMWD.Common.Logging namespace AMWD.Common.Logging
{ {
/// <summary> /// <summary>

View File

@@ -8,7 +8,7 @@ namespace AMWD.Common.Packing.Ar
/// Writes UNIX ar (archive) files in the GNU format. /// Writes UNIX ar (archive) files in the GNU format.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Copied from <a href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Ar/ArWriter.cs"/> /// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Ar/ArWriter.cs">DotnetMakeDeb</see>
/// </remarks> /// </remarks>
public class ArWriter public class ArWriter
{ {

View File

@@ -7,28 +7,24 @@ using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar namespace AMWD.Common.Packing.Tar
{ {
/// <summary> /// <summary>
/// Extract contents of a tar file represented by a stream for the TarReader constructor /// Extract contents of a tar file represented by a stream for the TarReader constructor.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarReader.cs /// Constructs TarReader object to read data from `tarredData` stream.
/// <br />
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarReader.cs">DotnetMakeDeb</see>
/// </remarks> /// </remarks>
public class TarReader /// <param name="tarredData">A stream to read tar archive from</param>
public class TarReader(Stream tarredData)
{ {
private readonly byte[] _dataBuffer = new byte[512]; private readonly byte[] _dataBuffer = new byte[512];
private readonly UsTarHeader _header; private readonly UsTarHeader _header = new();
private readonly Stream _inStream; private readonly Stream _inStream = tarredData;
private long _remainingBytesInFile; private long _remainingBytesInFile;
/// <summary> /// <summary>
/// Constructs TarReader object to read data from `tarredData` stream /// Gets the file info (the header).
/// </summary> /// </summary>
/// <param name="tarredData">A stream to read tar archive from</param>
public TarReader(Stream tarredData)
{
_inStream = tarredData;
_header = new UsTarHeader();
}
public ITarHeader FileInfo => _header; public ITarHeader FileInfo => _header;
/// <summary> /// <summary>
@@ -66,7 +62,7 @@ namespace AMWD.Common.Packing.Tar
} }
/// <summary> /// <summary>
/// Read data from a current file to a Stream. /// Read data from the current archive to a Stream.
/// </summary> /// </summary>
/// <param name="dataDestanation">A stream to read data to</param> /// <param name="dataDestanation">A stream to read data to</param>
/// <seealso cref="MoveNext"/> /// <seealso cref="MoveNext"/>
@@ -84,6 +80,11 @@ namespace AMWD.Common.Packing.Tar
Debug.WriteLine("tar stream position Read out: " + _inStream.Position); Debug.WriteLine("tar stream position Read out: " + _inStream.Position);
} }
/// <summary>
/// Reads data from the current archive to a buffer array.
/// </summary>
/// <param name="buffer">The buffer array.</param>
/// <returns>The nuber of bytes read.</returns>
protected int Read(out byte[] buffer) protected int Read(out byte[] buffer)
{ {
if (_remainingBytesInFile == 0) if (_remainingBytesInFile == 0)

View File

@@ -5,13 +5,32 @@ using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar namespace AMWD.Common.Packing.Tar
{ {
// https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs /// <summary>
/// Writes a tar (see GNU tar) archive to a stream.
/// </summary>
/// <remarks>
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs">DotnetMakeDeb</see>
/// </remarks>
public class TarWriter : LegacyTarWriter public class TarWriter : LegacyTarWriter
{ {
/// <summary>
/// Initilizes a new instance of the <see cref="TarWriter"/> class.
/// </summary>
/// <param name="outStream">The stream to write the archive to.</param>
public TarWriter(Stream outStream) public TarWriter(Stream outStream)
: base(outStream) : base(outStream)
{ } { }
/// <summary>
/// Writes an entry header (file, dir, ...) to the archive.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="entryType">The entry type.</param>
protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType) protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
{ {
var tarHeader = new UsTarHeader() var tarHeader = new UsTarHeader()
@@ -29,6 +48,17 @@ namespace AMWD.Common.Packing.Tar
OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize); OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize);
} }
/// <summary>
/// Writes an entry header (file, dir, ...) to the archive.
/// Hashes the username and groupname down to a HashCode.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userName">The username.</param>
/// <param name="groupName">The group name.</param>
/// <param name="mode">The access mode.</param>
/// <param name="entryType">The entry type.</param>
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode, EntryType entryType) protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode, EntryType entryType)
{ {
WriteHeader( WriteHeader(
@@ -41,6 +71,16 @@ namespace AMWD.Common.Packing.Tar
entryType: entryType); entryType: entryType);
} }
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
/// <param name="userName">The username.</param>
/// <param name="groupName">The group name.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="writeDelegate">The write handle.</param>
public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate) public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
{ {
var writer = new DataWriter(OutStream, dataSizeInBytes); var writer = new DataWriter(OutStream, dataSizeInBytes);
@@ -52,6 +92,16 @@ namespace AMWD.Common.Packing.Tar
AlignTo512(dataSizeInBytes, false); AlignTo512(dataSizeInBytes, false);
} }
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="data">The file stream to add to the archive.</param>
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
/// <param name="fileName">The file name.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification time.</param>
public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode, public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode,
DateTime lastModificationTime) DateTime lastModificationTime)
{ {

View File

@@ -10,11 +10,14 @@ namespace AMWD.Common.Packing.Tar.Utils
/// Implements a legacy TAR writer. /// Implements a legacy TAR writer.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Copied from <a href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/LegacyTarWriter.cs" /> /// Writes tar (see GNU tar) archive to a stream
/// <br/>
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/LegacyTarWriter.cs">DotnetMakeDeb</see>
/// </remarks> /// </remarks>
public class LegacyTarWriter : IDisposable /// <param name="outStream">stream to write archive to</param>
public class LegacyTarWriter(Stream outStream) : IDisposable
{ {
private readonly Stream _outStream; private readonly Stream _outStream = outStream;
private bool _isClosed; private bool _isClosed;
/// <summary> /// <summary>
@@ -22,15 +25,6 @@ namespace AMWD.Common.Packing.Tar.Utils
/// </summary> /// </summary>
protected byte[] buffer = new byte[1024]; protected byte[] buffer = new byte[1024];
/// <summary>
/// Writes tar (see GNU tar) archive to a stream
/// </summary>
/// <param name="outStream">stream to write archive to</param>
public LegacyTarWriter(Stream outStream)
{
_outStream = outStream;
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to read on zero. /// Gets or sets a value indicating whether to read on zero.
/// </summary> /// </summary>
@@ -48,20 +42,25 @@ namespace AMWD.Common.Packing.Tar.Utils
/// <inheritdoc/> /// <inheritdoc/>
public void Dispose() public void Dispose()
{ => Close();
Close();
}
#endregion IDisposable Members #endregion IDisposable Members
/// <summary>
/// Writes a directory entry.
/// </summary>
/// <param name="path">The path to the directory.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is not set.</exception>
public void WriteDirectoryEntry(string path, int userId, int groupId, int mode) public void WriteDirectoryEntry(string path, int userId, int groupId, int mode)
{ {
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
throw new ArgumentNullException("path"); throw new ArgumentNullException(nameof(path), "The path is not set.");
if (path[path.Length - 1] != '/') if (path[path.Length - 1] != '/')
{
path += '/'; path += '/';
}
DateTime lastWriteTime; DateTime lastWriteTime;
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
@@ -89,40 +88,50 @@ namespace AMWD.Common.Packing.Tar.Utils
WriteHeader(path, lastWriteTime, 0, userId, groupId, mode, EntryType.Directory); WriteHeader(path, lastWriteTime, 0, userId, groupId, mode, EntryType.Directory);
} }
/// <summary>
/// Writes a directory and its contents.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="doRecursive">Write also sub-directories.</param>
/// <exception cref="ArgumentNullException"><paramref name="directory"/> is not set.</exception>
public void WriteDirectory(string directory, bool doRecursive) public void WriteDirectory(string directory, bool doRecursive)
{ {
if (string.IsNullOrEmpty(directory)) if (string.IsNullOrEmpty(directory))
throw new ArgumentNullException("directory"); throw new ArgumentNullException(nameof(directory), "The directory is not set.");
WriteDirectoryEntry(directory, 0, 0, 0755); WriteDirectoryEntry(directory, 0, 0, 0755);
string[] files = Directory.GetFiles(directory); string[] files = Directory.GetFiles(directory);
foreach (string fileName in files) foreach (string fileName in files)
{
Write(fileName); Write(fileName);
}
string[] directories = Directory.GetDirectories(directory); string[] directories = Directory.GetDirectories(directory);
foreach (string dirName in directories) foreach (string dirName in directories)
{ {
WriteDirectoryEntry(dirName, 0, 0, 0755); WriteDirectoryEntry(dirName, 0, 0, 0755);
if (doRecursive) if (doRecursive)
{
WriteDirectory(dirName, true); WriteDirectory(dirName, true);
}
} }
} }
/// <summary>
/// Writes a file.
/// </summary>
/// <param name="fileName">The file.</param>
/// <exception cref="ArgumentNullException"><paramref name="fileName"/> is not set.</exception>
public void Write(string fileName) public void Write(string fileName)
{ {
if (string.IsNullOrEmpty(fileName)) if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName"); throw new ArgumentNullException(nameof(fileName), "The file name is not set.");
using (FileStream file = File.OpenRead(fileName))
{ using var fileStream = File.OpenRead(fileName);
Write(file, file.Length, fileName, 61, 61, 511, File.GetLastWriteTime(file.Name)); Write(fileStream, fileStream.Length, fileName, 61, 61, 511, File.GetLastWriteTime(fileStream.Name));
}
} }
/// <summary>
/// Writes a file stream.
/// </summary>
/// <param name="file">The file stream.</param>
public void Write(FileStream file) public void Write(FileStream file)
{ {
string path = Path.GetFullPath(file.Name).Replace(Path.GetPathRoot(file.Name), string.Empty); string path = Path.GetFullPath(file.Name).Replace(Path.GetPathRoot(file.Name), string.Empty);
@@ -130,22 +139,48 @@ namespace AMWD.Common.Packing.Tar.Utils
Write(file, file.Length, path, 61, 61, 511, File.GetLastWriteTime(file.Name)); Write(file, file.Length, path, 61, 61, 511, File.GetLastWriteTime(file.Name));
} }
/// <summary>
/// Writes a stream.
/// </summary>
/// <param name="data">The contents.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="name">The file name.</param>
public void Write(Stream data, long dataSizeInBytes, string name) public void Write(Stream data, long dataSizeInBytes, string name)
{ => Write(data, dataSizeInBytes, name, 61, 61, 511, DateTime.Now);
Write(data, dataSizeInBytes, name, 61, 61, 511, DateTime.Now);
}
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification timestamp.</param>
/// <param name="writeDelegate">The <see cref="WriteDataDelegate"/>.</param>
public virtual void Write(string name, long dataSizeInBytes, int userId, int groupId, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate) public virtual void Write(string name, long dataSizeInBytes, int userId, int groupId, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
{ {
IArchiveDataWriter writer = new DataWriter(OutStream, dataSizeInBytes); var writer = new DataWriter(OutStream, dataSizeInBytes);
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File); WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
while (writer.CanWrite) while (writer.CanWrite)
{
writeDelegate(writer); writeDelegate(writer);
}
AlignTo512(dataSizeInBytes, false); AlignTo512(dataSizeInBytes, false);
} }
/// <summary>
/// Writes a stream as file to the archive.
/// </summary>
/// <param name="data">The content as <see cref="Stream"/>.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="name">The file name.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification timestamp.</param>
/// <exception cref="TarException">This writer is already closed.</exception>
public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, DateTime lastModificationTime) public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, DateTime lastModificationTime)
{ {
if (_isClosed) if (_isClosed)
@@ -189,21 +224,26 @@ namespace AMWD.Common.Packing.Tar.Utils
Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0); Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0);
// add a "././@LongLink" pseudo-entry which contains the full name // add a "././@LongLink" pseudo-entry which contains the full name
using (var nameStream = new MemoryStream(entryName)) using var nameStream = new MemoryStream(entryName);
{ WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName);
WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName); WriteContent(entryName.Length, nameStream);
WriteContent(entryName.Length, nameStream); AlignTo512(entryName.Length, false);
AlignTo512(entryName.Length, false);
}
} }
/// <summary>
/// Writes a stream as file to the archive.
/// </summary>
/// <param name="count">The size of the file in bytes.</param>
/// <param name="data">The file content as stream.</param>
/// <exception cref="IOException"><paramref name="data"/> has not enough to read from.</exception>
protected void WriteContent(long count, Stream data) protected void WriteContent(long count, Stream data)
{ {
while (count > 0 && count > buffer.Length) while (count > 0 && count > buffer.Length)
{ {
int bytesRead = data.Read(buffer, 0, buffer.Length); int bytesRead = data.Read(buffer, 0, buffer.Length);
if (bytesRead < 0) if (bytesRead < 0)
throw new IOException("LegacyTarWriter unable to read from provided stream"); throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0) if (bytesRead == 0)
{ {
if (ReadOnZero) if (ReadOnZero)
@@ -218,7 +258,8 @@ namespace AMWD.Common.Packing.Tar.Utils
{ {
int bytesRead = data.Read(buffer, 0, (int)count); int bytesRead = data.Read(buffer, 0, (int)count);
if (bytesRead < 0) if (bytesRead < 0)
throw new IOException("LegacyTarWriter unable to read from provided stream"); throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0) if (bytesRead == 0)
{ {
while (count > 0) while (count > 0)
@@ -232,6 +273,16 @@ namespace AMWD.Common.Packing.Tar.Utils
} }
} }
/// <summary>
/// Writes a entry header to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The file mode.</param>
/// <param name="entryType">The entry type</param>
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType) protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
{ {
var header = new TarHeader var header = new TarHeader
@@ -247,6 +298,9 @@ namespace AMWD.Common.Packing.Tar.Utils
OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize); OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize);
} }
/// <summary>
/// Aligns the entry to 512 bytes.
/// </summary>
public void AlignTo512(long size, bool acceptZero) public void AlignTo512(long size, bool acceptZero)
{ {
size %= 512; size %= 512;
@@ -258,6 +312,9 @@ namespace AMWD.Common.Packing.Tar.Utils
} }
} }
/// <summary>
/// Closes the writer and aligns to 512 bytes.
/// </summary>
public virtual void Close() public virtual void Close()
{ {
if (_isClosed) if (_isClosed)

View File

@@ -2,10 +2,51 @@
namespace AMWD.Common.Packing.Tar.Utils namespace AMWD.Common.Packing.Tar.Utils
{ {
/// <summary>
/// Represents errors that occur during tar archive execution.
/// </summary>
public class TarException : Exception public class TarException : Exception
{ {
public TarException(string message) : base(message) /// <summary>
{ /// Initializes a new instance of the <see cref="TarException"/> class.
} /// </summary>
public TarException()
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="TarException"/> class with a specified
/// error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public TarException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the System.Exception class with a specified error
/// message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
/// if no inner exception is specified.</param>
public TarException(string message, Exception innerException)
: base(message, innerException)
{ }
#if !NET8_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TarException"/> class with serialized data.
/// </summary>
/// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/> that holds the serialized
/// object data about the exception being thrown.</param>
/// <param name="context">The <see cref="System.Runtime.Serialization.StreamingContext"/> that contains contextual information
/// about the source or destination.</param>
protected TarException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{ }
#endif
} }
} }

View File

@@ -94,7 +94,7 @@ namespace System.Collections.Generic
/// Determines whether an element is in the <see cref="AsyncQueue{T}"/>. /// Determines whether an element is in the <see cref="AsyncQueue{T}"/>.
/// </summary> /// </summary>
/// <param name="item">The object to locate in the <see cref="AsyncQueue{T}"/>. The value can be null for reference types.</param> /// <param name="item">The object to locate in the <see cref="AsyncQueue{T}"/>. The value can be null for reference types.</param>
/// <returns>true if item is found in the <see cref="AsyncQueue{T}"/>; otherwise, false.</returns> /// <returns><see langword="true"/> if item is found in the <see cref="AsyncQueue{T}"/>, otherwise <see langword="false"/>.</returns>
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public bool Contains(T item) public bool Contains(T item)
{ {
@@ -172,7 +172,7 @@ namespace System.Collections.Generic
{ {
lock (_queue) lock (_queue)
{ {
return _queue.ToArray(); return [.. _queue];
} }
} }
@@ -304,7 +304,7 @@ namespace System.Collections.Generic
/// Removes the object at the beginning of the <see cref="AsyncQueue{T}"/>, and copies it to the <paramref name="result"/> parameter. /// Removes the object at the beginning of the <see cref="AsyncQueue{T}"/>, and copies it to the <paramref name="result"/> parameter.
/// </summary> /// </summary>
/// <param name="result">The removed object.</param> /// <param name="result">The removed object.</param>
/// <returns>true if the object is successfully removed; false if the <see cref="AsyncQueue{T}"/> is empty.</returns> /// <returns><see langword="true"/> if the object is successfully removed, <see langword="false"/> if the <see cref="AsyncQueue{T}"/> is empty.</returns>
public bool TryDequeue(out T result) public bool TryDequeue(out T result)
{ {
try try
@@ -325,7 +325,7 @@ namespace System.Collections.Generic
/// <paramref name="result"/> parameter. The object is not removed from the <see cref="AsyncQueue{T}"/>. /// <paramref name="result"/> parameter. The object is not removed from the <see cref="AsyncQueue{T}"/>.
/// </summary> /// </summary>
/// <param name="result">If present, the object at the beginning of the <see cref="AsyncQueue{T}"/>; otherwise, the default value of <typeparamref name="T"/>.</param> /// <param name="result">If present, the object at the beginning of the <see cref="AsyncQueue{T}"/>; otherwise, the default value of <typeparamref name="T"/>.</param>
/// <returns>true if there is an object at the beginning of the <see cref="AsyncQueue{T}"/>; false if the <see cref="AsyncQueue{T}"/> is empty.</returns> /// <returns><see langword="true"/> if there is an object at the beginning of the <see cref="AsyncQueue{T}"/>, <see langword="false"/> if the <see cref="AsyncQueue{T}"/> is empty.</returns>
public bool TryPeek(out T result) public bool TryPeek(out T result)
{ {
try try
@@ -344,7 +344,7 @@ namespace System.Collections.Generic
/// Removes the first occurrence of a specific object from the <see cref="AsyncQueue{T}"/>. /// Removes the first occurrence of a specific object from the <see cref="AsyncQueue{T}"/>.
/// </summary> /// </summary>
/// <param name="item">The object to remove from the <see cref="AsyncQueue{T}"/>. The value can be null for reference types.</param> /// <param name="item">The object to remove from the <see cref="AsyncQueue{T}"/>. The value can be null for reference types.</param>
/// <returns>true if item is successfully removed; otherwise, false. This method also returns false if item was not found in the <see cref="AsyncQueue{T}"/>.</returns> /// <returns><see langword="true"/> if item is successfully removed, otherwise <see langword="false"/>. This method also returns <see langword="false"/> if item was not found in the <see cref="AsyncQueue{T}"/>.</returns>
public bool Remove(T item) public bool Remove(T item)
{ {
lock (_queue) lock (_queue)

View File

@@ -171,6 +171,7 @@ namespace System.Security.Cryptography
#region Static methods #region Static methods
#region Encryption #region Encryption
#pragma warning disable SYSLIB0041
#region AES #region AES
@@ -185,7 +186,11 @@ namespace System.Security.Cryptography
byte[] salt = new byte[_saltLength]; byte[] salt = new byte[_saltLength];
Array.Copy(cipher, salt, _saltLength); Array.Copy(cipher, salt, _saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt); #if NET8_0_OR_GREATER
using var gen = new Rfc2898DeriveBytes(password, salt, 1000, HashAlgorithmName.SHA1);
#else
using var gen = new Rfc2898DeriveBytes(password, salt, 1000);
#endif
using var aes = Aes.Create(); using var aes = Aes.Create();
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
@@ -225,7 +230,11 @@ namespace System.Security.Cryptography
{ {
byte[] salt = GetRandomBytes(_saltLength); byte[] salt = GetRandomBytes(_saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt); #if NET8_0_OR_GREATER
using var gen = new Rfc2898DeriveBytes(password, salt, 1000, HashAlgorithmName.SHA1);
#else
using var gen = new Rfc2898DeriveBytes(password, salt, 1000);
#endif
using var aes = Aes.Create(); using var aes = Aes.Create();
aes.Mode = CipherMode.CBC; aes.Mode = CipherMode.CBC;
@@ -271,7 +280,11 @@ namespace System.Security.Cryptography
byte[] salt = new byte[_saltLength]; byte[] salt = new byte[_saltLength];
Array.Copy(cipher, salt, _saltLength); Array.Copy(cipher, salt, _saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt); #if NET8_0_OR_GREATER
using var gen = new Rfc2898DeriveBytes(password, salt, 1000, HashAlgorithmName.SHA1);
#else
using var gen = new Rfc2898DeriveBytes(password, salt, 1000);
#endif
using var tdes = TripleDES.Create(); using var tdes = TripleDES.Create();
tdes.Mode = CipherMode.CBC; tdes.Mode = CipherMode.CBC;
@@ -298,7 +311,11 @@ namespace System.Security.Cryptography
{ {
byte[] salt = GetRandomBytes(_saltLength); byte[] salt = GetRandomBytes(_saltLength);
using var gen = new Rfc2898DeriveBytes(password, salt); #if NET8_0_OR_GREATER
using var gen = new Rfc2898DeriveBytes(password, salt, 1000, HashAlgorithmName.SHA1);
#else
using var gen = new Rfc2898DeriveBytes(password, salt, 1000);
#endif
using var tdes = TripleDES.Create(); using var tdes = TripleDES.Create();
tdes.Mode = CipherMode.CBC; tdes.Mode = CipherMode.CBC;
@@ -344,6 +361,7 @@ namespace System.Security.Cryptography
#endregion Triple DES #endregion Triple DES
#pragma warning restore SYSLIB0041
#endregion Encryption #endregion Encryption
#region Hashing #region Hashing
@@ -379,8 +397,12 @@ namespace System.Security.Cryptography
/// <returns>The MD5 hash value, in hexadecimal notation.</returns> /// <returns>The MD5 hash value, in hexadecimal notation.</returns>
public static string Md5(byte[] bytes) public static string Md5(byte[] bytes)
{ {
#if NET8_0_OR_GREATER
return MD5.HashData(bytes).BytesToHex();
#else
using var md5 = MD5.Create(); using var md5 = MD5.Create();
return md5.ComputeHash(bytes).BytesToHex(); return md5.ComputeHash(bytes).BytesToHex();
#endif
} }
#endregion MD5 #endregion MD5
@@ -416,8 +438,12 @@ namespace System.Security.Cryptography
/// <returns>The SHA-1 hash value, in hexadecimal notation.</returns> /// <returns>The SHA-1 hash value, in hexadecimal notation.</returns>
public static string Sha1(byte[] bytes) public static string Sha1(byte[] bytes)
{ {
#if NET8_0_OR_GREATER
return SHA1.HashData(bytes).BytesToHex();
#else
using var sha1 = SHA1.Create(); using var sha1 = SHA1.Create();
return sha1.ComputeHash(bytes).BytesToHex(); return sha1.ComputeHash(bytes).BytesToHex();
#endif
} }
#endregion SHA-1 #endregion SHA-1
@@ -453,8 +479,12 @@ namespace System.Security.Cryptography
/// <returns>The SHA-256 hash value, in hexadecimal notation.</returns> /// <returns>The SHA-256 hash value, in hexadecimal notation.</returns>
public static string Sha256(byte[] bytes) public static string Sha256(byte[] bytes)
{ {
#if NET8_0_OR_GREATER
return SHA256.HashData(bytes).BytesToHex();
#else
using var sha256 = SHA256.Create(); using var sha256 = SHA256.Create();
return sha256.ComputeHash(bytes).BytesToHex(); return sha256.ComputeHash(bytes).BytesToHex();
#endif
} }
#endregion SHA-256 #endregion SHA-256
@@ -490,8 +520,12 @@ namespace System.Security.Cryptography
/// <returns>The SHA-512 hash value, in hexadecimal notation.</returns> /// <returns>The SHA-512 hash value, in hexadecimal notation.</returns>
public static string Sha512(byte[] bytes) public static string Sha512(byte[] bytes)
{ {
#if NET8_0_OR_GREATER
return SHA512.HashData(bytes).BytesToHex();
#else
using var sha512 = SHA512.Create(); using var sha512 = SHA512.Create();
return sha512.ComputeHash(bytes).BytesToHex(); return sha512.ComputeHash(bytes).BytesToHex();
#endif
} }
#endregion SHA-512 #endregion SHA-512

View File

@@ -4,7 +4,12 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace AMWD.Common.Utilities namespace AMWD.Common.Utilities
{ {
@@ -23,7 +28,7 @@ namespace AMWD.Common.Utilities
public static List<IPAddress> ResolveHost(string hostname, AddressFamily addressFamily = default) public static List<IPAddress> ResolveHost(string hostname, AddressFamily addressFamily = default)
{ {
if (string.IsNullOrWhiteSpace(hostname)) if (string.IsNullOrWhiteSpace(hostname))
return new(); return [];
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6) if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
addressFamily = AddressFamily.Unspecified; addressFamily = AddressFamily.Unspecified;
@@ -31,7 +36,7 @@ namespace AMWD.Common.Utilities
var ipAddress = ResolveIpAddress(hostname, addressFamily); var ipAddress = ResolveIpAddress(hostname, addressFamily);
// the name was an ip address, should not happen but experience tells other stories // the name was an ip address, should not happen but experience tells other stories
if (ipAddress != null) if (ipAddress != null)
return new() { ipAddress }; return [ipAddress];
try try
{ {
@@ -41,7 +46,7 @@ namespace AMWD.Common.Utilities
} }
catch catch
{ {
return new(); return [];
} }
} }
@@ -54,7 +59,7 @@ namespace AMWD.Common.Utilities
public static List<IPAddress> ResolveInterface(string interfaceName, AddressFamily addressFamily = default) public static List<IPAddress> ResolveInterface(string interfaceName, AddressFamily addressFamily = default)
{ {
if (string.IsNullOrWhiteSpace(interfaceName)) if (string.IsNullOrWhiteSpace(interfaceName))
return new(); return [];
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6) if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
addressFamily = AddressFamily.Unspecified; addressFamily = AddressFamily.Unspecified;
@@ -62,7 +67,7 @@ namespace AMWD.Common.Utilities
var ipAddress = ResolveIpAddress(interfaceName, addressFamily); var ipAddress = ResolveIpAddress(interfaceName, addressFamily);
// the name was an ip address, should not happen but experience tells other stories // the name was an ip address, should not happen but experience tells other stories
if (ipAddress != null) if (ipAddress != null)
return new() { ipAddress }; return [ipAddress];
try try
{ {
@@ -74,45 +79,7 @@ namespace AMWD.Common.Utilities
} }
catch catch
{ {
return new(); return [];
}
}
/// <summary>
/// Parses a CIDR network definition.
/// </summary>
/// <param name="network">The network in CIDR.</param>
/// <returns>The <see cref="IPNetwork"/> or <c>null</c>.</returns>
public static IPNetwork ParseNetwork(string network)
{
TryParseNetwork(network, out var ipNetwork);
return ipNetwork;
}
/// <summary>
/// Tries to parse a CIDR network definition.
/// </summary>
/// <param name="network">The network in CIDR.</param>
/// <param name="ipNetwork">The parsed <see cref="IPNetwork"/>.</param>
/// <returns><c>true</c> on success, otherwise <c>false</c>.</returns>
public static bool TryParseNetwork(string network, out IPNetwork ipNetwork)
{
try
{
string[] parts = network.Split('/');
if (parts.Length != 2)
throw new ArgumentException($"Invalid network type");
var prefix = IPAddress.Parse(parts.First());
int prefixLength = int.Parse(parts.Last());
ipNetwork = new IPNetwork(prefix, prefixLength);
return true;
}
catch
{
ipNetwork = null;
return false;
} }
} }
@@ -128,7 +95,11 @@ namespace AMWD.Common.Utilities
{ {
var list = new List<IPAddress>(); var list = new List<IPAddress>();
#if NET8_0_OR_GREATER
var ipAddress = network.BaseAddress;
#else
var ipAddress = network.Prefix; var ipAddress = network.Prefix;
#endif
while (network.Contains(ipAddress)) while (network.Contains(ipAddress))
{ {
list.Add(ipAddress); list.Add(ipAddress);

View File

@@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16
nuget.config = nuget.config nuget.config = nuget.config
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.MessagePack", "AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -67,6 +69,10 @@ Global
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU {9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU {9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU {9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -80,6 +86,7 @@ Global
{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB} {86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{7196DA2B-D858-4B25-BC23-865175CFCDEC} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB} {7196DA2B-D858-4B25-BC23-865175CFCDEC} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{93EC8B16-7DEF-4E39-B590-E804DEF7C607} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB} {93EC8B16-7DEF-4E39-B590-E804DEF7C607} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC} SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}

View File

@@ -4,7 +4,6 @@
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory> <CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
@@ -34,7 +33,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AMWD.NetRevisionTask" Version="1.1.1"> <PackageReference Include="AMWD.NetRevisionTask" Version="1.2.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using AMWD.Common.AspNetCore.Security.BasicAuthentication; using AMWD.Common.AspNetCore.Security.BasicAuthentication;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq; using Moq;
@@ -156,12 +157,16 @@ namespace UnitTests.AspNetCore.Security.BasicAuthentication
var requestHeaderMock = new Mock<IHeaderDictionary>(); var requestHeaderMock = new Mock<IHeaderDictionary>();
foreach (var header in _requestHeaders) foreach (var header in _requestHeaders)
{ {
var strVal = new StringValues(header.Value);
requestHeaderMock requestHeaderMock
.Setup(h => h.ContainsKey(header.Key)) .Setup(h => h.ContainsKey(header.Key))
.Returns(true); .Returns(true);
requestHeaderMock requestHeaderMock
.Setup(h => h[header.Key]) .Setup(h => h[header.Key])
.Returns(header.Value); .Returns(strVal);
requestHeaderMock
.Setup(h => h.TryGetValue(header.Key, out strVal))
.Returns(true);
} }
var requestMock = new Mock<HttpRequest>(); var requestMock = new Mock<HttpRequest>();
@@ -174,6 +179,11 @@ namespace UnitTests.AspNetCore.Security.BasicAuthentication
responseHeaderMock responseHeaderMock
.SetupSet(h => h[It.IsAny<string>()] = It.IsAny<StringValues>()) .SetupSet(h => h[It.IsAny<string>()] = It.IsAny<StringValues>())
.Callback<string, StringValues>((key, value) => _responseHeadersCallback[key] = value); .Callback<string, StringValues>((key, value) => _responseHeadersCallback[key] = value);
#pragma warning disable CS0618
responseHeaderMock
.SetupSet(h => h.WWWAuthenticate)
.Callback((value) => _responseHeadersCallback[HeaderNames.WWWAuthenticate] = value);
#pragma warning restore CS0618
var responseMock = new Mock<HttpResponse>(); var responseMock = new Mock<HttpResponse>();
responseMock responseMock

View File

@@ -108,22 +108,6 @@ namespace UnitTests.Common.Extensions
Assert.IsFalse(list.Any()); Assert.IsFalse(list.Any());
} }
[TestMethod]
public void ShouldReturnDisplayNameOrStringRepresentation()
{
// arrange
var enumWithDisplayName = TestEnum.Two;
var enumWithoutDisplayName = TestEnum.Zero;
// act
string displayName = enumWithDisplayName.GetDisplayName();
string noDisplayName = enumWithoutDisplayName.GetDisplayName();
// assert
Assert.AreEqual("Zwei", displayName);
Assert.AreEqual(enumWithoutDisplayName.ToString(), noDisplayName);
}
internal enum TestEnum internal enum TestEnum
{ {
[CustomMultiple("nix")] [CustomMultiple("nix")]
@@ -132,7 +116,6 @@ namespace UnitTests.Common.Extensions
Zero, Zero,
[Description("Eins")] [Description("Eins")]
One, One,
[Display(Name = "Zwei")]
Two, Two,
} }
} }

View File

@@ -12,16 +12,16 @@ namespace UnitTests.Common.Packing.Ar
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class ArReaderTests public class ArReaderTests
{ {
private readonly DateTime fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc); private readonly DateTime _fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc);
private Dictionary<string, ArFileInfo> files; private Dictionary<string, ArFileInfo> _files;
private Stream inStream; private Stream inStream;
[TestInitialize] [TestInitialize]
public void Initialize() public void Initialize()
{ {
files = new Dictionary<string, ArFileInfo> _files = new Dictionary<string, ArFileInfo>
{ {
{ {
"abcd.tmp", "abcd.tmp",
@@ -31,7 +31,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 14, FileSize = 14,
GroupId = 456, GroupId = 456,
Mode = 33188, Mode = 33188,
ModifyTime = fixedDateTime, ModifyTime = _fixedDateTime,
UserId = 123 UserId = 123
} }
}, },
@@ -43,7 +43,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 14, FileSize = 14,
GroupId = 456, GroupId = 456,
Mode = 33188, Mode = 33188,
ModifyTime = fixedDateTime, ModifyTime = _fixedDateTime,
UserId = 123 UserId = 123
} }
}, },
@@ -55,7 +55,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 13, FileSize = 13,
GroupId = 456, GroupId = 456,
Mode = 33188, Mode = 33188,
ModifyTime = fixedDateTime, ModifyTime = _fixedDateTime,
UserId = 123 UserId = 123
} }
} }
@@ -64,7 +64,7 @@ namespace UnitTests.Common.Packing.Ar
inStream = new MemoryStream(); inStream = new MemoryStream();
inStream.Write(Encoding.ASCII.GetBytes("!<arch>\n")); inStream.Write(Encoding.ASCII.GetBytes("!<arch>\n"));
foreach (var file in files) foreach (var file in _files)
{ {
int unixSeconds = (int)file.Value.ModifyTime.Subtract(DateTime.UnixEpoch).TotalSeconds; int unixSeconds = (int)file.Value.ModifyTime.Subtract(DateTime.UnixEpoch).TotalSeconds;
@@ -125,9 +125,9 @@ namespace UnitTests.Common.Packing.Ar
// Assert // Assert
Assert.IsNotNull(reader); Assert.IsNotNull(reader);
Assert.AreEqual(files.Count, fileList.Count); Assert.AreEqual(_files.Count, fileList.Count);
foreach (string name in files.Keys) foreach (string name in _files.Keys)
Assert.IsTrue(fileList.Contains(name)); Assert.IsTrue(fileList.Contains(name));
} }
@@ -139,14 +139,14 @@ namespace UnitTests.Common.Packing.Ar
var reader = new ArReader(inStream); var reader = new ArReader(inStream);
// Act // Act
foreach (string name in files.Keys) foreach (string name in _files.Keys)
infos.Add(reader.GetFileInfo(name)); infos.Add(reader.GetFileInfo(name));
// Assert // Assert
Assert.IsNotNull(reader); Assert.IsNotNull(reader);
Assert.AreEqual(files.Count, infos.Count); Assert.AreEqual(_files.Count, infos.Count);
foreach (var expected in files.Values) foreach (var expected in _files.Values)
{ {
var actual = infos.Single(fi => fi.FileName == expected.FileName); var actual = infos.Single(fi => fi.FileName == expected.FileName);
@@ -167,7 +167,7 @@ namespace UnitTests.Common.Packing.Ar
var reader = new ArReader(inStream); var reader = new ArReader(inStream);
// Act // Act
foreach (string name in files.Keys) foreach (string name in _files.Keys)
{ {
using var ms = new MemoryStream(); using var ms = new MemoryStream();
reader.ReadFile(name, ms); reader.ReadFile(name, ms);
@@ -178,9 +178,9 @@ namespace UnitTests.Common.Packing.Ar
// Assert // Assert
Assert.IsNotNull(reader); Assert.IsNotNull(reader);
Assert.AreEqual(files.Count, contents.Count); Assert.AreEqual(_files.Count, contents.Count);
foreach (var expected in files.Values) foreach (var expected in _files.Values)
{ {
string content = contents[expected.FileName]; string content = contents[expected.FileName];

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnitTests.Common.Packing.Tar
{
internal class TarReaderTests
{
}
}

View File

@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<LangVersion>12.0</LangVersion>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<CollectCoverage>true</CollectCoverage> <CollectCoverage>true</CollectCoverage>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
@@ -13,8 +14,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="DNS" Version="7.0.0" /> <PackageReference Include="DNS" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.69" /> <PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" /> <PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="ReflectionMagic" Version="5.0.0" /> <PackageReference Include="ReflectionMagic" Version="5.0.0" />