Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
40 changes: 40 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CodeQL

on:
pull_request:
branches: [ "main", "develop" ]
schedule:
- cron: '0 0 * * 1'

jobs:
analyze:
name: Analyze (C#)
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: csharp

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
10.0.x

- name: Build
working-directory: ./src
run: dotnet build

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:csharp"
59 changes: 59 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: PR Validation

on:
pull_request:
branches: [ "main", "develop" ]

jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
10.0.x

- name: Restore
working-directory: ./test/Unit
run: dotnet restore

- name: Build
working-directory: ./test/Unit
run: dotnet build --no-restore

- name: Test
working-directory: ./test/Unit
run: dotnet test --no-build --verbosity normal

integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
10.0.x

- name: Start services
run: docker compose -f test/Integration/docker-compose.yml up -d --wait

- name: Restore
working-directory: ./test/Integration
run: dotnet restore

- name: Build
working-directory: ./test/Integration
run: dotnet build --no-restore

- name: Test
working-directory: ./test/Integration
run: dotnet test -p:TestTfmsInParallel=false --no-build --verbosity normal
76 changes: 76 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Release

on:
push:
branches:
- 'release/**'

jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Setup Redis
uses: supercharge/redis-github-action@1.8.1
with:
redis-version: 7

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
10.0.x

- name: Run unit tests
working-directory: ./test/Unit
run: dotnet test --verbosity normal

- name: Run integration tests
working-directory: ./test/Integration
run: dotnet test --verbosity normal

publish:
name: Pack and Publish
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Extract version from branch name
id: version
run: |
BRANCH="${GITHUB_REF#refs/heads/}"
VERSION="${BRANCH#release/v}"
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "Invalid version format in branch name: $BRANCH (expected release/vX.Y.Z)"
exit 1
fi
echo "value=$VERSION" >> "$GITHUB_OUTPUT"

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
10.0.x

- name: Pack
working-directory: ./src
run: >
dotnet pack
--configuration Release
-p:Version=${{ steps.version.outputs.value }}
--output ./nupkg

- name: Push to NuGet
working-directory: ./src
run: >
dotnet nuget push ./nupkg/*.nupkg
--api-key ${{ secrets.NUGET_API_KEY }}
--source https://api.nuget.org/v3/index.json
--skip-duplicate
115 changes: 60 additions & 55 deletions src/ActionCache.csproj
Original file line number Diff line number Diff line change
@@ -1,55 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup>
<PackageId>ActionCache</PackageId>
<Version>0.0.9</Version>
<Authors>Joshua Zillwood</Authors>
<Description>A simple yet powerful data caching library that adds an extra layer of caching to your ASP.NET Core applications.</Description>
<Company></Company>
<PackageIcon>Icon.jpg</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/jzills/ActionCache</PackageProjectUrl>
<RepositoryUrl>https://github.com/jzills/ActionCache.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>mvc;cache;azure;cosmos;sqlserver;redis;memory</PackageTags>
<Copyright>Copyright © Joshua Zillwood</Copyright>
</PropertyGroup>

<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<None Include="../resources/Icon.jpg" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Unit" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Redis/Scripts/SetHash.lua" />
<EmbeddedResource Include="Redis/Scripts/Remove.lua" />
<EmbeddedResource Include="Redis/Scripts/RemoveNamespace.lua" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.46.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup>
<PackageId>ActionCache</PackageId>
<Version>0.0.9</Version>
<Authors>Joshua Zillwood</Authors>
<Description>A simple yet powerful data caching library that adds an extra layer of caching to your ASP.NET Core applications.</Description>
<Company></Company>
<PackageIcon>Icon.jpg</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/jzills/ActionCache</PackageProjectUrl>
<RepositoryUrl>https://github.com/jzills/ActionCache.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>mvc;cache;azure;cosmos;sqlserver;redis;memory</PackageTags>
<Copyright>Copyright © Joshua Zillwood</Copyright>
</PropertyGroup>

<PropertyGroup>
<_TargetVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.0</_TargetVersion>
<_TargetVersion Condition="'$(TargetFramework)' == 'net10.0'">10.0.0</_TargetVersion>
</PropertyGroup>

<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<None Include="../resources/Icon.jpg" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Unit" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Redis/Scripts/SetHash.lua" />
<EmbeddedResource Include="Redis/Scripts/Remove.lua" />
<EmbeddedResource Include="Redis/Scripts/RemoveNamespace.lua" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.46.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="$(_TargetVersion)" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(_TargetVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

</Project>
8 changes: 4 additions & 4 deletions src/Common/Caching/ActionCacheEntryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public class ActionCacheEntryOptions
/// <summary>
/// Gets or sets the duration for which the lock will remain valid once acquired.
/// </summary>
/// <value>The default is 200 milliseconds.</value>
public TimeSpan LockDuration { get; set; } = TimeSpan.FromMilliseconds(200);
/// <value>The default is 5 seconds.</value>
public TimeSpan LockDuration { get; set; } = TimeSpan.FromSeconds(5);

/// <summary>
/// Gets or sets the maximum amount of time to wait for acquiring the lock before timing out.
/// </summary>
/// <value>The default is 200 milliseconds.</value>
public TimeSpan LockTimeout { get; set; } = TimeSpan.FromMilliseconds(200);
/// <value>The default is 10 seconds.</value>
public TimeSpan LockTimeout { get; set; } = TimeSpan.FromSeconds(10);

/// <summary>
/// Calculates the absolute expiration date and time based on <see cref="AbsoluteExpiration"/>, relative to the current UTC time.
Expand Down
4 changes: 1 addition & 3 deletions src/SqlServer/SqlServerActionCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
/// <typeparam name="TValue">The type of the cached value.</typeparam>
/// <param name="key">The key of the cache entry.</param>
/// <returns>The cached value or the default value of the type if not found.</returns>
public override async Task<TValue> GetAsync<TValue>(string key)

Check warning on line 48 in src/SqlServer/SqlServerActionCache.cs

View workflow job for this annotation

GitHub Actions / Analyze (C#)

Nullability of reference types in return type doesn't match overridden member.

Check warning on line 48 in src/SqlServer/SqlServerActionCache.cs

View workflow job for this annotation

GitHub Actions / Integration Tests

Nullability of reference types in return type doesn't match overridden member.
{
var json = await Cache.GetStringAsync(Namespace.Create(key));
if (string.IsNullOrWhiteSpace(json))
Expand All @@ -64,7 +64,7 @@
/// <typeparam name="TValue">The type of the value to set in the cache.</typeparam>
/// <param name="key">The cache key to set the value for.</param>
/// <param name="value">The value to set in the cache.</param>
public override async Task SetAsync<TValue>(string key, TValue value)

Check warning on line 67 in src/SqlServer/SqlServerActionCache.cs

View workflow job for this annotation

GitHub Actions / Analyze (C#)

Nullability of type of parameter 'value' doesn't match overridden member (possibly because of nullability attributes).

Check warning on line 67 in src/SqlServer/SqlServerActionCache.cs

View workflow job for this annotation

GitHub Actions / Integration Tests

Nullability of type of parameter 'value' doesn't match overridden member (possibly because of nullability attributes).
{
var entryOptions = CreateEntryOptions();
await Cache.SetStringAsync(Namespace.Create(key), CacheJsonSerializer.Serialize(value), entryOptions);
Expand All @@ -91,9 +91,7 @@
public override async Task RemoveAsync()
{
var keys = await GetKeysAsync();

await CacheLocker.WaitForLockThenAsync(Namespace,
() => Task.WhenAll(keys.Select(RemoveAsync)));
await Task.WhenAll(keys.Select(RemoveAsync));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using ActionCache;
using Integration.TestUtilities.Data;
using Microsoft.Extensions.DependencyInjection;

[TestFixture]
public class Test_ActionCache_Expiration_Absolute
{
IActionCache Cache;

[Test]
[TestCaseSource(typeof(TestData), nameof(TestData.GetServiceProviders))]
public async Task Test_GetAsync_Expires(IServiceProvider serviceProvider)
{
var cacheFactory = serviceProvider.GetRequiredService<IActionCacheFactory>();
Cache = cacheFactory.Create(nameof(Test_GetAsync_Expires), TimeSpan.FromSeconds(5));

await Cache.SetAsync("Key_Expiration_1", "Value_1");
var result = await Cache.GetAsync<string?>("Key_Expiration_1");
var keys = await Cache.GetKeysAsync();

Assert.That(result, Is.EqualTo("Value_1"));
Assert.That(keys.Count(), Is.EqualTo(1));

Thread.Sleep(10000);

result = await Cache.GetAsync<string?>("Key_Expiration_1");
keys = await Cache.GetKeysAsync();

Assert.That(result, Is.Null);
Assert.That(keys.Count(), Is.EqualTo(0));
}

[Test]
[TestCaseSource(typeof(TestData), nameof(TestData.GetServiceProviders))]
public async Task Test_GetKeys_Expires(IServiceProvider serviceProvider)
{
var cacheFactory = serviceProvider.GetRequiredService<IActionCacheFactory>();
Cache = cacheFactory.Create(nameof(Test_GetKeys_Expires), TimeSpan.FromSeconds(5));

await Cache.SetAsync("Key_Expiration_1", "Value_1");
var result = await Cache.GetAsync<string?>("Key_Expiration_1");
var keys = await Cache.GetKeysAsync();

Assert.That(result, Is.EqualTo("Value_1"));
Assert.That(keys.Count(), Is.EqualTo(1));

Thread.Sleep(10000);

keys = await Cache.GetKeysAsync();
result = await Cache.GetAsync<string?>("Key_Expiration_1");

Assert.That(result, Is.Null);
Assert.That(keys.Count(), Is.EqualTo(0));
}

[TearDown]
public async Task TearDown()
{
await Cache.RemoveAsync();
}
}
Loading
Loading