diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 800c12f..65b3c8a 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -12,7 +12,7 @@ on:
- cron: '0 0 1,15 * *'
env:
- NET_VERSION: '7.x'
+ NET_VERSION: '9.x'
jobs:
analyze:
@@ -44,7 +44,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,7 +57,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@v3
# ?? Command-line programs to run using the OS shell.
# ?? See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -70,4 +70,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index 55f738d..44119ae 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -40,7 +40,7 @@ jobs:
# Run Linter against code base #
################################
- name: Lint Code Base
- uses: github/super-linter@v4
+ uses: super-linter/super-linter@v7.2.0
env:
LINTER_RULES_PATH: '.'
EDITORCONFIG_FILE_NAME: '.editorconfig'
diff --git a/samples/TinyHelpers.AspNetCore.Sample/TinyHelpers.AspNetCore.Sample.csproj b/samples/TinyHelpers.AspNetCore.Sample/TinyHelpers.AspNetCore.Sample.csproj
index 693ba9c..18cbc87 100644
--- a/samples/TinyHelpers.AspNetCore.Sample/TinyHelpers.AspNetCore.Sample.csproj
+++ b/samples/TinyHelpers.AspNetCore.Sample/TinyHelpers.AspNetCore.Sample.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/samples/TinyHelpers.AspNetCore8.Sample/TinyHelpers.AspNetCore8.Sample.csproj b/samples/TinyHelpers.AspNetCore8.Sample/TinyHelpers.AspNetCore8.Sample.csproj
index 3c033f9..e3e73de 100644
--- a/samples/TinyHelpers.AspNetCore8.Sample/TinyHelpers.AspNetCore8.Sample.csproj
+++ b/samples/TinyHelpers.AspNetCore8.Sample/TinyHelpers.AspNetCore8.Sample.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiOperationOptions.cs b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiOperationOptions.cs
new file mode 100644
index 0000000..1a7ef42
--- /dev/null
+++ b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiOperationOptions.cs
@@ -0,0 +1,12 @@
+using Microsoft.OpenApi.Models;
+
+namespace TinyHelpers.AspNetCore.Swagger;
+
+public class OpenApiOperationOptions
+{
+ internal OpenApiOperationOptions()
+ {
+ }
+
+ public IList Parameters { get; } = [];
+}
diff --git a/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiParametersOperationFilter.cs b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiParametersOperationFilter.cs
index 1077339..9efdf03 100644
--- a/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiParametersOperationFilter.cs
+++ b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiParametersOperationFilter.cs
@@ -1,5 +1,4 @@
-using Microsoft.OpenApi.Any;
-using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace TinyHelpers.AspNetCore.Swagger;
@@ -8,7 +7,7 @@ internal class OpenApiParametersOperationFilter(OpenApiOperationOptions options)
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
- if (options?.Parameters.Count > 0)
+ if (options.Parameters.Count > 0)
{
operation.Parameters ??= [];
@@ -18,74 +17,4 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context)
}
}
}
-}
-
-public class OpenApiOperationOptions
-{
- internal OpenApiOperationOptions()
- {
- }
-
- public IList Parameters { get; init; } = [];
-}
-
-public static class OpenApiSchemaHelper
-{
- public static OpenApiSchema CreateStringSchema(string? defaultValue = null)
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(string type, string? format = null)
- {
- var schema = new OpenApiSchema
- {
- Type = type,
- Format = format
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(string type, string? format, TValue? defaultValue = null) where TValue : struct
- {
- var schema = new OpenApiSchema
- {
- Type = type,
- Format = format,
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(IEnumerable values, string? defaultValue = null)
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Enum = values.Select(v => new OpenApiString(v)).Cast().ToList(),
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(TEnum? defaultValue = null) where TEnum : struct, Enum
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Enum = Enum.GetValues().Select(e => new OpenApiString(e.ToString())).Cast().ToList(),
- Default = defaultValue.HasValue ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
}
\ No newline at end of file
diff --git a/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiSchemaHelper.cs b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiSchemaHelper.cs
new file mode 100644
index 0000000..2e08a8c
--- /dev/null
+++ b/src/TinyHelpers.AspNetCore.Swashbuckle/OpenApiSchemaHelper.cs
@@ -0,0 +1,65 @@
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+
+namespace TinyHelpers.AspNetCore.Swagger;
+
+public static class OpenApiSchemaHelper
+{
+ public static OpenApiSchema CreateStringSchema(string? defaultValue = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(string type, string? format = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = type,
+ Format = format
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(string type, string? format, TValue? defaultValue = null) where TValue : struct
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = type,
+ Format = format,
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(IEnumerable values, string? defaultValue = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Enum = values.Select(v => new OpenApiString(v)).Cast().ToList(),
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(TEnum? defaultValue = null) where TEnum : struct, Enum
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Enum = Enum.GetValues().Select(e => new OpenApiString(e.ToString())).Cast().ToList(),
+ Default = defaultValue.HasValue ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+}
\ No newline at end of file
diff --git a/src/TinyHelpers.AspNetCore.Swashbuckle/TinyHelpers.AspNetCore.Swashbuckle.csproj b/src/TinyHelpers.AspNetCore.Swashbuckle/TinyHelpers.AspNetCore.Swashbuckle.csproj
index 9785afa..7acf96f 100644
--- a/src/TinyHelpers.AspNetCore.Swashbuckle/TinyHelpers.AspNetCore.Swashbuckle.csproj
+++ b/src/TinyHelpers.AspNetCore.Swashbuckle/TinyHelpers.AspNetCore.Swashbuckle.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/src/TinyHelpers.AspNetCore/OpenApi/OpenApiOperationOptions.cs b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiOperationOptions.cs
new file mode 100644
index 0000000..49dc3ff
--- /dev/null
+++ b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiOperationOptions.cs
@@ -0,0 +1,16 @@
+#if NET9_0_OR_GREATER
+
+using Microsoft.OpenApi.Models;
+
+namespace TinyHelpers.AspNetCore.OpenApi;
+
+public class OpenApiOperationOptions
+{
+ internal OpenApiOperationOptions()
+ {
+ }
+
+ public IList Parameters { get; } = [];
+}
+
+#endif
\ No newline at end of file
diff --git a/src/TinyHelpers.AspNetCore/OpenApi/OpenApiParametersOperationTransformer.cs b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiParametersOperationTransformer.cs
index 7c91b66..512b35b 100644
--- a/src/TinyHelpers.AspNetCore/OpenApi/OpenApiParametersOperationTransformer.cs
+++ b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiParametersOperationTransformer.cs
@@ -1,7 +1,6 @@
#if NET9_0_OR_GREATER
using Microsoft.AspNetCore.OpenApi;
-using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
namespace TinyHelpers.AspNetCore.OpenApi;
@@ -10,7 +9,7 @@ internal class OpenApiParametersOperationFilter(OpenApiOperationOptions options)
{
public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransformerContext context, CancellationToken cancellationToken)
{
- if (options?.Parameters.Count > 0)
+ if (options.Parameters.Count > 0)
{
operation.Parameters ??= [];
@@ -24,74 +23,4 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
}
}
-public class OpenApiOperationOptions
-{
- internal OpenApiOperationOptions()
- {
- }
-
- public IList Parameters { get; init; } = [];
-}
-
-public static class OpenApiSchemaHelper
-{
- public static OpenApiSchema CreateStringSchema(string? defaultValue = null)
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(string type, string? format = null)
- {
- var schema = new OpenApiSchema
- {
- Type = type,
- Format = format
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(string type, string? format, TValue? defaultValue = null) where TValue : struct
- {
- var schema = new OpenApiSchema
- {
- Type = type,
- Format = format,
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(IEnumerable values, string? defaultValue = null)
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Enum = values.Select(v => new OpenApiString(v)).Cast().ToList(),
- Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-
- public static OpenApiSchema CreateSchema(TEnum? defaultValue = null) where TEnum : struct, Enum
- {
- var schema = new OpenApiSchema
- {
- Type = "string",
- Enum = Enum.GetValues().Select(e => new OpenApiString(e.ToString())).Cast().ToList(),
- Default = defaultValue.HasValue ? new OpenApiString(defaultValue.ToString()) : null
- };
-
- return schema;
- }
-}
-
#endif
\ No newline at end of file
diff --git a/src/TinyHelpers.AspNetCore/OpenApi/OpenApiSchemaHelper.cs b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiSchemaHelper.cs
new file mode 100644
index 0000000..ca0073b
--- /dev/null
+++ b/src/TinyHelpers.AspNetCore/OpenApi/OpenApiSchemaHelper.cs
@@ -0,0 +1,69 @@
+#if NET9_0_OR_GREATER
+
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+
+namespace TinyHelpers.AspNetCore.OpenApi;
+
+public static class OpenApiSchemaHelper
+{
+ public static OpenApiSchema CreateStringSchema(string? defaultValue = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(string type, string? format = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = type,
+ Format = format
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(string type, string? format, TValue? defaultValue = null) where TValue : struct
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = type,
+ Format = format,
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(IEnumerable values, string? defaultValue = null)
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Enum = values.Select(v => new OpenApiString(v)).Cast().ToList(),
+ Default = defaultValue is not null ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+
+ public static OpenApiSchema CreateSchema(TEnum? defaultValue = null) where TEnum : struct, Enum
+ {
+ var schema = new OpenApiSchema
+ {
+ Type = "string",
+ Enum = Enum.GetValues().Select(e => new OpenApiString(e.ToString())).Cast().ToList(),
+ Default = defaultValue.HasValue ? new OpenApiString(defaultValue.ToString()) : null
+ };
+
+ return schema;
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/src/TinyHelpers.Dapper/TypeHandlers/TimeSpanTypeHandler.cs b/src/TinyHelpers.Dapper/TypeHandlers/TimeSpanTypeHandler.cs
new file mode 100644
index 0000000..dadef5e
--- /dev/null
+++ b/src/TinyHelpers.Dapper/TypeHandlers/TimeSpanTypeHandler.cs
@@ -0,0 +1,30 @@
+using System.Data;
+using System.Globalization;
+using Dapper;
+
+namespace TinyHelpers.Dapper.TypeHandlers;
+
+public class TimeSpanTypeHandler : SqlMapper.TypeHandler
+{
+ public override TimeSpan Parse(object value)
+ {
+ return value switch
+ {
+ null => TimeSpan.Zero,
+ string stringValue => TimeSpan.Parse(stringValue, CultureInfo.InvariantCulture),
+ long longValue => TimeSpan.FromTicks(longValue),
+ int intValue => TimeSpan.FromSeconds(intValue),
+ double doubleValue => TimeSpan.FromSeconds(doubleValue),
+ _ => throw new ArgumentException($"Unable to convert {value.GetType()} to {nameof(TimeSpan)}"),
+ };
+ }
+
+ public override void SetValue(IDbDataParameter parameter, TimeSpan value)
+ {
+ parameter.Value = value.Ticks;
+ parameter.DbType = DbType.Int64;
+ }
+
+ public static void Configure()
+ => SqlMapper.AddTypeHandler(new TimeSpanTypeHandler());
+}
diff --git a/src/TinyHelpers/Threading/AsyncLock.cs b/src/TinyHelpers/Threading/AsyncLock.cs
index ee1829c..f219ec1 100644
--- a/src/TinyHelpers/Threading/AsyncLock.cs
+++ b/src/TinyHelpers/Threading/AsyncLock.cs
@@ -8,7 +8,7 @@ public class AsyncLock : IDisposable
private readonly SemaphoreSlim semaphoreSlim = new(1, 1);
///
- /// Asyncronously waits for the lock to become available.
+ /// Asynchronously waits for the lock to become available.
///
/// A token that can be used to request cancellation of the asynchronous operation.
/// An awaitable task.
@@ -18,6 +18,38 @@ public async Task LockAsync(CancellationToken cancellationToken = def
return this;
}
+ ///
+ /// Asynchronously waits for the lock to become available, with a specific timeout in milliseconds.
+ ///
+ /// The number of milliseconds to wait, or (-1) to wait indefinitely.
+ /// A token that can be used to request cancellation of the asynchronous operation.
+ /// An awaitable task that returns a indicating whether the lock was acquired or not and the lock itself.
+ ///
+ /// is a negative number other than -1 milliseconds, which represents
+ /// an infinite timeout.
+ ///
+ public async Task LockAsync(int timeoutInMilliseconds, CancellationToken cancellationToken = default)
+ {
+ var isOwned = await semaphoreSlim.WaitAsync(timeoutInMilliseconds, cancellationToken).ConfigureAwait(false);
+ return new LockResult(isOwned, isOwned ? this : null);
+ }
+
+ ///
+ /// Asynchronously waits for the lock to become available, using a to specify a timeout.
+ ///
+ /// A that represents the maximum time to wait
+ /// A token that can be used to request cancellation of the asynchronous operation.
+ /// An awaitable task that returns a indicating whether the lock was acquired or not and the lock itself.
+ ///
+ /// is a negative number other than -1 milliseconds, which represents
+ /// an infinite timeout or timeout is greater than .
+ ///
+ public async Task LockAsync(TimeSpan timeout, CancellationToken cancellationToken = default)
+ {
+ var isOwned = await semaphoreSlim.WaitAsync(timeout, cancellationToken).ConfigureAwait(false);
+ return new LockResult(isOwned, isOwned ? this : null);
+ }
+
///
/// Releases the lock.
///
diff --git a/src/TinyHelpers/Threading/LockResult.cs b/src/TinyHelpers/Threading/LockResult.cs
new file mode 100644
index 0000000..3a3305f
--- /dev/null
+++ b/src/TinyHelpers/Threading/LockResult.cs
@@ -0,0 +1,30 @@
+namespace TinyHelpers.Threading;
+
+///
+/// Represents the result of an asynchronous lock operation.
+///
+public readonly struct LockResult
+{
+ ///
+ /// Gets the object if succesfully acquired.
+ ///
+ public AsyncLock? AsyncLock { get; }
+
+ ///
+ /// Gets a boolean indicating if the lock was acquired or not.
+ ///
+ public bool IsOwned { get; }
+
+ internal LockResult(bool isOwned, AsyncLock? asyncLock)
+ {
+ (IsOwned, AsyncLock) = (isOwned, asyncLock);
+ }
+
+ ///
+ /// Deconstruct the object.
+ ///
+ /// The boolean indicating if the lock was acquired or not.
+ /// The object.
+ public void Deconstruct(out bool isOwned, out AsyncLock? asyncLock)
+ => (isOwned, asyncLock) = (IsOwned, AsyncLock);
+}