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/*.snupkg ./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/*.snupkg ./artifacts/
artifacts:
@@ -72,7 +74,9 @@ build-release:
- mv ./AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Release/*.snupkg ./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/*.snupkg ./artifacts/
artifacts:
@@ -136,6 +140,19 @@ deploy-entityframework:
- if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/
script:
- 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:
stage: deploy

View File

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

View File

@@ -12,12 +12,12 @@ namespace Microsoft.AspNetCore.Http
public static class HttpContextExtensions
{
// 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
"X-Real-IP", // wide-spread alternative to X-Forwarded-For
"X-Forwarded-For", // commonly used on all known proxies
};
];
/// <summary>
/// Retrieves the antiforgery token.

View File

@@ -9,26 +9,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <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.
/// </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]
public class InvariantFloatingPointModelBinder : IModelBinder
public class InvariantFloatingPointModelBinder(NumberStyles supportedStyles, CultureInfo cultureInfo, ILoggerFactory loggerFactory)
: IModelBinder
{
private readonly NumberStyles _supportedNumberStyles;
private readonly ILogger _logger;
private readonly CultureInfo _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>();
}
private readonly NumberStyles _supportedNumberStyles = supportedStyles;
private readonly ILogger _logger = loggerFactory?.CreateLogger<InvariantFloatingPointModelBinder>();
private readonly CultureInfo _cultureInfo = cultureInfo ?? throw new ArgumentNullException(nameof(cultureInfo));
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)

View File

@@ -13,49 +13,39 @@ using Microsoft.Extensions.Options;
namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
{
#if NET8_0_OR_GREATER
/// <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="validator">An basic autentication validator implementation.</param>
[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 IBasicAuthenticationValidator _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
private readonly ILogger _logger = logger.CreateLogger<BasicAuthenticationHandler>();
private readonly IBasicAuthenticationValidator _validator = validator;
/// <inheritdoc/>
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()

View File

@@ -11,21 +11,15 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
/// <summary>
/// Implements a basic authentication.
/// </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 IBasicAuthenticationValidator _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;
}
private readonly RequestDelegate _next = next;
private readonly IBasicAuthenticationValidator _validator = validator;
/// <summary>
/// The delegate invokation.
@@ -35,15 +29,27 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
/// <returns>An awaitable task.</returns>
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"))
{
SetAuthenticateRequest(httpContext, _validator.Realm);
return;
}
#endif
try
{
#if NET8_0_OR_GREATER
var authHeader = AuthenticationHeaderValue.Parse(authHeaderValue);
#else
var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]);
#endif
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
string plain = Encoding.UTF8.GetString(decoded);
@@ -70,9 +76,9 @@ namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
private static void SetAuthenticateRequest(HttpContext httpContext, string realm)
{
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
httpContext.Response.Headers.WWWAuthenticate = "Basic";
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;
}

View File

@@ -8,23 +8,16 @@ namespace AMWD.Common.AspNetCore.Security.PathProtection
/// <summary>
/// Implements a check to provide protected paths.
/// </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 PathString _path;
private readonly string _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;
}
private readonly RequestDelegate _next = next;
private readonly PathString _path = options.Path;
private readonly string _policyName = options.PolicyName;
/// <summary>
/// The delegate invokation.

View File

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

View File

@@ -13,24 +13,19 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary>
/// A tag helper to dynamically create integrity checks for linked sources.
/// </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]
[HtmlTargetElement("link")]
[HtmlTargetElement("script")]
public class IntegrityHashTagHelper : TagHelper
public class IntegrityHashTagHelper(IWebHostEnvironment env, IConfiguration configuration)
: TagHelper
{
private readonly IWebHostEnvironment _env;
private readonly string _hostUrl;
/// <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/");
}
private readonly IWebHostEnvironment _env = env;
private readonly string _hostUrl = configuration.GetValue("ASPNETCORE_APPL_URL", "http://localhost/");
/// <summary>
/// Gets or sets a value indicating whether the integrity should be calculated.
@@ -118,7 +113,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
}
string type;
byte[] hashBytes = Array.Empty<byte>();
byte[] hashBytes = [];
switch (IntegrityStrength)
{
case 512:

View File

@@ -9,17 +9,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <summary>
/// Adds additional behavior to the modelbinding for numeric properties.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="NumberInputTagHelper"/> class.
/// </remarks>
/// <param name="generator">The HTML generator.</param>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[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 />
public override void Process(TagHelperContext context, TagHelperOutput output)

View File

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

View File

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

View File

@@ -15,7 +15,11 @@ namespace Microsoft.EntityFrameworkCore
/// <summary>
/// Extensions for the <see cref="DatabaseFacade"/>.
/// </summary>
#if NET8_0_OR_GREATER
public static partial class DatabaseFacadeExtensions
#else
public static class DatabaseFacadeExtensions
#endif
{
/// <summary>
/// Applies migration files to the database.
@@ -23,7 +27,7 @@ namespace Microsoft.EntityFrameworkCore
/// <param name="database">The database connection.</param>
/// <param name="optionsAction">An action to set additional options.</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")]
public static async Task<bool> ApplyMigrationsAsync(this DatabaseFacade database, Action<DatabaseMigrationOptions> optionsAction, CancellationToken cancellationToken = default)
{
@@ -211,20 +215,20 @@ END;"
if (options.SourceAssembly == null)
{
availableMigrationFiles = Directory.GetFiles(options.Path)
.Where(f => f.ToLower().StartsWith(options.Path.ToLower()))
.Where(f => f.ToLower().EndsWith(".sql"))
.Where(f => f.StartsWith(options.Path, StringComparison.OrdinalIgnoreCase))
.Where(f => f.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
.ToList();
}
else
{
availableMigrationFiles = options.SourceAssembly
.GetManifestResourceNames()
.Where(f => f.ToLower().StartsWith(options.Path.ToLower()))
.Where(f => f.ToLower().EndsWith(".sql"))
.Where(f => f.StartsWith(options.Path, StringComparison.OrdinalIgnoreCase))
.Where(f => f.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
.ToList();
}
if (!availableMigrationFiles.Any())
if (availableMigrationFiles.Count == 0)
return true;
using var command = connection.CreateCommand();
@@ -270,7 +274,11 @@ END;"
{
using var stream = options.SourceAssembly.GetManifestResourceStream(migrationFile);
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);
#endif
}
if (string.IsNullOrWhiteSpace(sqlScript))
@@ -316,7 +324,11 @@ END;"
{
int affectedRows = 0;
// 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");
#endif
foreach (string part in parts)
{
// Make writable copy
@@ -325,7 +337,11 @@ END;"
// Remove the trailing semicolon from commands where they're not supported
// (Oracle doesn't like semicolons. To keep the semicolon, it must be directly
// 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
if (!string.IsNullOrWhiteSpace(pt))
@@ -352,5 +368,13 @@ END;"
SQLServer = 5,
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>
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)
throw new ArgumentNullException(nameof(optionsBuilder));
if (configuration == null)
throw new ArgumentNullException(nameof(configuration));
#endif
var options = new DatabaseProviderOptions();
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.Linq;
using System.Net;
using AMWD.Common.Utilities;
using AMWD.Common.MessagePack.Utilities;
namespace MessagePack.Formatters
{
@@ -66,7 +66,7 @@ namespace MessagePack.Formatters
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.Linq;
using System.Net;
using AMWD.Common.Utilities;
using AMWD.Common.MessagePack.Utilities;
namespace MessagePack.Formatters
{
@@ -61,7 +61,7 @@ namespace MessagePack.Formatters
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.Collections.Generic;
using System.Linq;
using AMWD.Common.Utilities;
using MessagePack;
using MessagePack.Formatters;
using AMWD.Common.MessagePack.Utilities;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace AMWD.Common.Formatters
namespace MessagePack.Formatters
{
/// <summary>
/// Serialization of an <see cref="IPNetwork"/> array to and from <see cref="MessagePack"/>.

View File

@@ -1,7 +1,12 @@
using System;
using System.Linq;
using System.Net;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace MessagePack.Formatters
{
@@ -15,7 +20,7 @@ namespace MessagePack.Formatters
public IPNetwork Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.IsNil)
return null;
return default;
byte[] bytes = options.Resolver.GetFormatterWithVerify<byte[]>().Deserialize(ref reader, options);
return DeserializeInternal(bytes);
@@ -24,7 +29,7 @@ namespace MessagePack.Formatters
/// <inheritdoc/>
public void Serialize(ref MessagePackWriter writer, IPNetwork value, MessagePackSerializerOptions options)
{
if (value == null)
if (value == default)
{
writer.WriteNil();
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.
byte prefixLength = (byte)network.PrefixLength;
#if NET8_0_OR_GREATER
byte[] prefixBytes = network.BaseAddress.GetAddressBytes();
#else
byte[] prefixBytes = network.Prefix.GetAddressBytes();
#endif
byte[] bytes = new byte[prefixBytes.Length + 1];
bytes[0] = prefixLength;

View File

@@ -1,8 +1,13 @@
using System;
using System.Collections.Generic;
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 IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
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>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>test/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.Test</AssemblyName>
@@ -22,7 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
</ItemGroup>

View File

@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<AssemblyName>AMWD.Common</AssemblyName>
<RootNamespace>AMWD.Common</RootNamespace>
@@ -21,10 +21,23 @@
</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="Unclassified.DeepConvert" Version="1.4.0" />
</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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace System
@@ -41,13 +40,5 @@ namespace System
/// <returns>The description or the string representation of the value.</returns>
public static string GetDescription(this Enum value)
=> 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);
}
private struct DisposableReadWriteLock : IDisposable
private struct DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
: IDisposable
{
private readonly ReaderWriterLockSlim _rwLock;
private LockMode _lockMode;
public DisposableReadWriteLock(ReaderWriterLockSlim rwLock, LockMode lockMode)
{
_rwLock = rwLock;
_lockMode = lockMode;
}
private readonly ReaderWriterLockSlim _rwLock = rwLock;
private LockMode _lockMode = lockMode;
public void Dispose()
{

View File

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

View File

@@ -15,7 +15,12 @@ namespace System
/// <summary>
/// String extensions.
/// </summary>
#if NET8_0_OR_GREATER
public static partial class StringExtensions
#else
public static class StringExtensions
#endif
{
/// <summary>
/// Converts a hex string into a byte array.
@@ -32,8 +37,13 @@ namespace System
if (str.Length % 2 == 1)
yield break;
#if NET8_0_OR_GREATER
if (InvalidHexCharRegex().IsMatch(str))
yield break;
#else
if (Regex.IsMatch(str, "[^0-9a-fA-F]"))
yield break;
#endif
for (int i = 0; i < str.Length; i += 2)
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 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;
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();
object response = waitTask.Result;
@@ -232,5 +242,10 @@ namespace System
/// <returns></returns>
public static StringBuilder AppendLine(this StringBuilder sb, string value, string 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 Microsoft.Extensions.Logging;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
namespace AMWD.Common.Logging
{
/// <summary>

View File

@@ -8,7 +8,7 @@ namespace AMWD.Common.Packing.Ar
/// Writes UNIX ar (archive) files in the GNU format.
/// </summary>
/// <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>
public class ArWriter
{

View File

@@ -7,28 +7,24 @@ using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar
{
/// <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>
/// <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>
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 UsTarHeader _header;
private readonly Stream _inStream;
private readonly UsTarHeader _header = new();
private readonly Stream _inStream = tarredData;
private long _remainingBytesInFile;
/// <summary>
/// Constructs TarReader object to read data from `tarredData` stream
/// Gets the file info (the header).
/// </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;
/// <summary>
@@ -66,7 +62,7 @@ namespace AMWD.Common.Packing.Tar
}
/// <summary>
/// Read data from a current file to a Stream.
/// Read data from the current archive to a Stream.
/// </summary>
/// <param name="dataDestanation">A stream to read data to</param>
/// <seealso cref="MoveNext"/>
@@ -84,6 +80,11 @@ namespace AMWD.Common.Packing.Tar
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)
{
if (_remainingBytesInFile == 0)

View File

@@ -5,13 +5,32 @@ using AMWD.Common.Packing.Tar.Utils;
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
{
/// <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)
: 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)
{
var tarHeader = new UsTarHeader()
@@ -29,6 +48,17 @@ namespace AMWD.Common.Packing.Tar
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)
{
WriteHeader(
@@ -41,6 +71,16 @@ namespace AMWD.Common.Packing.Tar
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)
{
var writer = new DataWriter(OutStream, dataSizeInBytes);
@@ -52,6 +92,16 @@ namespace AMWD.Common.Packing.Tar
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,
DateTime lastModificationTime)
{

View File

@@ -10,11 +10,14 @@ namespace AMWD.Common.Packing.Tar.Utils
/// Implements a legacy TAR writer.
/// </summary>
/// <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>
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;
/// <summary>
@@ -22,15 +25,6 @@ namespace AMWD.Common.Packing.Tar.Utils
/// </summary>
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>
/// Gets or sets a value indicating whether to read on zero.
/// </summary>
@@ -48,20 +42,25 @@ namespace AMWD.Common.Packing.Tar.Utils
/// <inheritdoc/>
public void Dispose()
{
Close();
}
=> Close();
#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)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException("path");
throw new ArgumentNullException(nameof(path), "The path is not set.");
if (path[path.Length - 1] != '/')
{
path += '/';
}
DateTime lastWriteTime;
if (Directory.Exists(path))
{
@@ -89,40 +88,50 @@ namespace AMWD.Common.Packing.Tar.Utils
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)
{
if (string.IsNullOrEmpty(directory))
throw new ArgumentNullException("directory");
throw new ArgumentNullException(nameof(directory), "The directory is not set.");
WriteDirectoryEntry(directory, 0, 0, 0755);
string[] files = Directory.GetFiles(directory);
foreach (string fileName in files)
{
Write(fileName);
}
string[] directories = Directory.GetDirectories(directory);
foreach (string dirName in directories)
{
WriteDirectoryEntry(dirName, 0, 0, 0755);
if (doRecursive)
{
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)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException("fileName");
using (FileStream file = File.OpenRead(fileName))
{
Write(file, file.Length, fileName, 61, 61, 511, File.GetLastWriteTime(file.Name));
}
throw new ArgumentNullException(nameof(fileName), "The file name is not set.");
using var fileStream = File.OpenRead(fileName);
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)
{
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));
}
/// <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)
{
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)
{
IArchiveDataWriter writer = new DataWriter(OutStream, dataSizeInBytes);
var writer = new DataWriter(OutStream, dataSizeInBytes);
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
while (writer.CanWrite)
{
writeDelegate(writer);
}
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)
{
if (_isClosed)
@@ -189,21 +224,26 @@ namespace AMWD.Common.Packing.Tar.Utils
Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0);
// add a "././@LongLink" pseudo-entry which contains the full name
using (var nameStream = new MemoryStream(entryName))
{
WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName);
WriteContent(entryName.Length, nameStream);
AlignTo512(entryName.Length, false);
}
using var nameStream = new MemoryStream(entryName);
WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName);
WriteContent(entryName.Length, nameStream);
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)
{
while (count > 0 && count > buffer.Length)
{
int bytesRead = data.Read(buffer, 0, buffer.Length);
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 (ReadOnZero)
@@ -218,7 +258,8 @@ namespace AMWD.Common.Packing.Tar.Utils
{
int bytesRead = data.Read(buffer, 0, (int)count);
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)
{
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)
{
var header = new TarHeader
@@ -247,6 +298,9 @@ namespace AMWD.Common.Packing.Tar.Utils
OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize);
}
/// <summary>
/// Aligns the entry to 512 bytes.
/// </summary>
public void AlignTo512(long size, bool acceptZero)
{
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()
{
if (_isClosed)

View File

@@ -2,10 +2,51 @@
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// Represents errors that occur during tar archive execution.
/// </summary>
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}"/>.
/// </summary>
/// <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]
public bool Contains(T item)
{
@@ -172,7 +172,7 @@ namespace System.Collections.Generic
{
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.
/// </summary>
/// <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)
{
try
@@ -325,7 +325,7 @@ namespace System.Collections.Generic
/// <paramref name="result"/> parameter. The object is not removed from the <see cref="AsyncQueue{T}"/>.
/// </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>
/// <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)
{
try
@@ -344,7 +344,7 @@ namespace System.Collections.Generic
/// Removes the first occurrence of a specific object from the <see cref="AsyncQueue{T}"/>.
/// </summary>
/// <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)
{
lock (_queue)

View File

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

View File

@@ -4,7 +4,12 @@ using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
#if NET8_0_OR_GREATER
using IPNetwork = System.Net.IPNetwork;
#else
using Microsoft.AspNetCore.HttpOverrides;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;
#endif
namespace AMWD.Common.Utilities
{
@@ -23,7 +28,7 @@ namespace AMWD.Common.Utilities
public static List<IPAddress> ResolveHost(string hostname, AddressFamily addressFamily = default)
{
if (string.IsNullOrWhiteSpace(hostname))
return new();
return [];
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
addressFamily = AddressFamily.Unspecified;
@@ -31,7 +36,7 @@ namespace AMWD.Common.Utilities
var ipAddress = ResolveIpAddress(hostname, addressFamily);
// the name was an ip address, should not happen but experience tells other stories
if (ipAddress != null)
return new() { ipAddress };
return [ipAddress];
try
{
@@ -41,7 +46,7 @@ namespace AMWD.Common.Utilities
}
catch
{
return new();
return [];
}
}
@@ -54,7 +59,7 @@ namespace AMWD.Common.Utilities
public static List<IPAddress> ResolveInterface(string interfaceName, AddressFamily addressFamily = default)
{
if (string.IsNullOrWhiteSpace(interfaceName))
return new();
return [];
if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
addressFamily = AddressFamily.Unspecified;
@@ -62,7 +67,7 @@ namespace AMWD.Common.Utilities
var ipAddress = ResolveIpAddress(interfaceName, addressFamily);
// the name was an ip address, should not happen but experience tells other stories
if (ipAddress != null)
return new() { ipAddress };
return [ipAddress];
try
{
@@ -74,45 +79,7 @@ namespace AMWD.Common.Utilities
}
catch
{
return new();
}
}
/// <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;
return [];
}
}
@@ -128,7 +95,11 @@ namespace AMWD.Common.Utilities
{
var list = new List<IPAddress>();
#if NET8_0_OR_GREATER
var ipAddress = network.BaseAddress;
#else
var ipAddress = network.Prefix;
#endif
while (network.Contains(ipAddress))
{
list.Add(ipAddress);

View File

@@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.MessagePack", "AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -80,6 +86,7 @@ Global
{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {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}
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}

View File

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

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using AMWD.Common.AspNetCore.Security.BasicAuthentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
@@ -156,12 +157,16 @@ namespace UnitTests.AspNetCore.Security.BasicAuthentication
var requestHeaderMock = new Mock<IHeaderDictionary>();
foreach (var header in _requestHeaders)
{
var strVal = new StringValues(header.Value);
requestHeaderMock
.Setup(h => h.ContainsKey(header.Key))
.Returns(true);
requestHeaderMock
.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>();
@@ -174,6 +179,11 @@ namespace UnitTests.AspNetCore.Security.BasicAuthentication
responseHeaderMock
.SetupSet(h => h[It.IsAny<string>()] = It.IsAny<StringValues>())
.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>();
responseMock

View File

@@ -108,22 +108,6 @@ namespace UnitTests.Common.Extensions
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
{
[CustomMultiple("nix")]
@@ -132,7 +116,6 @@ namespace UnitTests.Common.Extensions
Zero,
[Description("Eins")]
One,
[Display(Name = "Zwei")]
Two,
}
}

View File

@@ -12,16 +12,16 @@ namespace UnitTests.Common.Packing.Ar
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
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;
[TestInitialize]
public void Initialize()
{
files = new Dictionary<string, ArFileInfo>
_files = new Dictionary<string, ArFileInfo>
{
{
"abcd.tmp",
@@ -31,7 +31,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 14,
GroupId = 456,
Mode = 33188,
ModifyTime = fixedDateTime,
ModifyTime = _fixedDateTime,
UserId = 123
}
},
@@ -43,7 +43,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 14,
GroupId = 456,
Mode = 33188,
ModifyTime = fixedDateTime,
ModifyTime = _fixedDateTime,
UserId = 123
}
},
@@ -55,7 +55,7 @@ namespace UnitTests.Common.Packing.Ar
FileSize = 13,
GroupId = 456,
Mode = 33188,
ModifyTime = fixedDateTime,
ModifyTime = _fixedDateTime,
UserId = 123
}
}
@@ -64,7 +64,7 @@ namespace UnitTests.Common.Packing.Ar
inStream = new MemoryStream();
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;
@@ -125,9 +125,9 @@ namespace UnitTests.Common.Packing.Ar
// Assert
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));
}
@@ -139,14 +139,14 @@ namespace UnitTests.Common.Packing.Ar
var reader = new ArReader(inStream);
// Act
foreach (string name in files.Keys)
foreach (string name in _files.Keys)
infos.Add(reader.GetFileInfo(name));
// Assert
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);
@@ -167,7 +167,7 @@ namespace UnitTests.Common.Packing.Ar
var reader = new ArReader(inStream);
// Act
foreach (string name in files.Keys)
foreach (string name in _files.Keys)
{
using var ms = new MemoryStream();
reader.ReadFile(name, ms);
@@ -178,9 +178,9 @@ namespace UnitTests.Common.Packing.Ar
// Assert
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];

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