Skip to content

Commit

Permalink
Adjusting interface to align with other parts of the .net framework a…
Browse files Browse the repository at this point in the history
…nd improving readme.
  • Loading branch information
raphaabreu committed Sep 30, 2020
1 parent 4a6ec7b commit e83ff77
Show file tree
Hide file tree
Showing 37 changed files with 153 additions and 148 deletions.
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SimpleConcepts.Extensions.Caching

This project provides caching related logic that is common to several of the projects that I have been a part of.
This package provides several extensions that make working with `IDistributedCache` easier, including Json object serialization, get with fallback, key space partitioning and logging. In addition to these extensions there is also a strongly typed `ISimpleCache<TKey, TValue>` interface that provides a dependency injection friendly and fully customizable wrapper for `IDistributedCache.`

Check the included project in `samples` to see a general purpose implementation.

## Installation

Expand All @@ -14,11 +16,7 @@ With .NET CLI:
dotnet add package SimpleConcepts.Extensions.Caching
```

## Samples

Check the included project in Sample.Web to see a general purpose implementation.

## Use cases
## Extensions

#### Json object serialization

Expand Down Expand Up @@ -69,7 +67,7 @@ This exemple can be rewritten in a single line:
```csharp
public static Task<Person> GetFromCacheOrFetchAsync(Guid personId)
{
return cache.GetOrFetchJsonObjectAsync($"person:{personId}",
return cache.GetOrSetJsonObjectAsync($"person:{personId}",
() => _personService.FetchAsync(personId), cacheEntryOptions);
}
```
Expand Down Expand Up @@ -108,11 +106,11 @@ All operations are logged with `Debug` when beginning and with `Information` or

## SimpleCache

The `ISimpleCache` interface and concrete types combine the extensions mentioned here in a simplified, configurable and dependency injection friendly package and provide a strongly-typed cache that can have custom serialization and default expiration options for all entries.
The `ISimpleCache` interface and the corresponding `SimpleCache` is a dependency injection friendly `IDistributedCache` wrapper that exposes simplified, configurable and strongly-typed methods that can have custom serialization and default expiration options for all entries.

The interface comes in two versions: `ISimpleCache<TKey, TValue>` and `ISimpleCache<TValue>`. The first is the most common usage cenario where you want to lookup values by a given key, the second is specific for cases where you have only a single value to be stored.

By default, it will:
By default an `ISimpleCache<TKey, TValue>` will:
* Serialize and deserialize `TValue` using `System.Text.Json`.
* Serialize the `TKey` with `key.ToString()`.
* Prefix all keys with `typeof(TValue).FullName + ":"`.
Expand Down Expand Up @@ -150,7 +148,7 @@ private async Task<IEnumerable<WeatherForecast>> FetchAllForecastsAsync(Cancella

// Get cached daily forecast if it exists and fetch if not.
var forecast = await _dailyForecastCache
.GetOrFetchAsync(date, () => FetchSingleForecastAsync(date, cancellationToken), cancellationToken);
.GetOrSetAsync(date, () => FetchSingleForecastAsync(date, cancellationToken), cancellationToken);

forecasts.Add(forecast);
}
Expand All @@ -163,6 +161,4 @@ private async Task<WeatherForecast> FetchSingleForecastAsync(DateTime date, Canc
// Logic for fetching key missing from cache
// ...
}
```

That is it.
```
17 changes: 14 additions & 3 deletions SimpleConcepts.Extensions.Caching.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29728.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConcepts.Extensions.Caching.Tests", "SimpleConcepts.Extensions.Caching.Tests\SimpleConcepts.Extensions.Caching.Tests.csproj", "{A0B92D7E-A88E-4115-A569-25ACE8481F68}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConcepts.Extensions.Caching.Tests", "tests\SimpleConcepts.Extensions.Caching.Tests\SimpleConcepts.Extensions.Caching.Tests.csproj", "{A0B92D7E-A88E-4115-A569-25ACE8481F68}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConcepts.Extensions.Caching", "SimpleConcepts.Extensions.Caching\SimpleConcepts.Extensions.Caching.csproj", "{531D00D9-92C6-4E94-92CB-E94B97E45335}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConcepts.Extensions.Caching", "src\SimpleConcepts.Extensions.Caching\SimpleConcepts.Extensions.Caching.csproj", "{531D00D9-92C6-4E94-92CB-E94B97E45335}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Web", "Sample.Web\Sample.Web.csproj", "{270D3C88-0150-45AD-B263-F7154ABAF3A9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Web", "samples\Sample.Web\Sample.Web.csproj", "{270D3C88-0150-45AD-B263-F7154ABAF3A9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{86BBB450-4870-4E89-B948-453A93B4D217}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E009AB93-C201-45BD-B4BB-F450F55F112F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{56462026-DB0C-4F3A-A4E9-185C32C9BA95}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{B1B8FE9E-ABDC-4B31-B535-1D88E04DECCE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -36,6 +42,11 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A0B92D7E-A88E-4115-A569-25ACE8481F68} = {B1B8FE9E-ABDC-4B31-B535-1D88E04DECCE}
{531D00D9-92C6-4E94-92CB-E94B97E45335} = {56462026-DB0C-4F3A-A4E9-185C32C9BA95}
{270D3C88-0150-45AD-B263-F7154ABAF3A9} = {E009AB93-C201-45BD-B4BB-F450F55F112F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {15C770B8-637D-429B-ACAC-7E6058EE356C}
EndGlobalSection
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task<IEnumerable<WeatherForecast>> GetAsync(CancellationToken cance
{
// Will get cached response if there is any and fetch otherwise.
var response = await _distributedCache
.GetOrFetchJsonObjectAsync("WeatherForecastController_Get",
.GetOrSetJsonObjectAsync("WeatherForecastController_Get",
() => FetchAllForecastsAsync(cancellationToken),
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5) },
cancellationToken);
Expand All @@ -55,7 +55,7 @@ private async Task<IEnumerable<WeatherForecast>> FetchAllForecastsAsync(Cancella

// Get cached daily forecast if it exists and fetch if not.
var forecast = await _distributedCache
.GetOrFetchJsonObjectAsync($"single-weather-forecast:{date.ToShortDateString()}",
.GetOrSetJsonObjectAsync($"single-weather-forecast:{date.ToShortDateString()}",
() => FetchSingleForecastAsync(date, cancellationToken),
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(15) },
cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public async Task<IEnumerable<WeatherForecast>> GetAsync(CancellationToken cance
{
// Will get cached response if there is any and fetch otherwise.
var response = await _responseCache
.GetOrFetchAsync(() => FetchAllForecastsAsync(cancellationToken), cancellationToken);
.GetOrSetAsync(() => FetchAllForecastsAsync(cancellationToken), cancellationToken);

return response;
}
Expand All @@ -55,7 +55,7 @@ private async Task<IEnumerable<WeatherForecast>> FetchAllForecastsAsync(Cancella

// Get cached daily forecast if it exists and fetch if not.
var forecast = await _dailyForecastCache
.GetOrFetchAsync(date, () => FetchSingleForecastAsync(date, cancellationToken), cancellationToken);
.GetOrSetAsync(date, () => FetchSingleForecastAsync(date, cancellationToken), cancellationToken);

forecasts.Add(forecast);
}
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\SimpleConcepts.Extensions.Caching\SimpleConcepts.Extensions.Caching.csproj" />
<ProjectReference Include="..\..\src\SimpleConcepts.Extensions.Caching\SimpleConcepts.Extensions.Caching.csproj" />
</ItemGroup>


Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using System.Threading;
using System.Threading.Tasks;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.Caching.Distributed
{
public static class DistributedCacheGetOrSetExtensions
{
public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byte[]> valueFactory)
{
return cache.GetOrSet(key, valueFactory, new DistributedCacheEntryOptions());
}

public static byte[] GetOrSet(this IDistributedCache cache, string key, Func<byte[]> valueFactory,
DistributedCacheEntryOptions options)
{
var cached = cache.Get(key);

if (cached != null)
{
return cached;
}

var value = valueFactory();

cache.Set(key, value, options);

return value;
}

public static Task<byte[]> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]>> valueFactory,
CancellationToken token = default)
{
return cache.GetOrSetAsync(key, valueFactory, new DistributedCacheEntryOptions(), token);
}

public static async Task<byte[]> GetOrSetAsync(this IDistributedCache cache, string key, Func<Task<byte[]>> valueFactory,
DistributedCacheEntryOptions options, CancellationToken token = default)
{
var cached = await cache.GetAsync(key, token);

if (cached != null)
{
return cached;
}

var value = await valueFactory();

await cache.SetAsync(key, value, options, token);

return value;
}

public static string GetOrSetString(this IDistributedCache cache, string key, Func<string> valueFactory)
{
return cache.GetOrSetString(key, valueFactory, new DistributedCacheEntryOptions());
}

public static string GetOrSetString(this IDistributedCache cache, string key, Func<string> valueFactory,
DistributedCacheEntryOptions options)
{
var cached = cache.GetString(key);

if (cached != null)
{
return cached;
}

var value = valueFactory();

cache.SetString(key, value, options);

return value;
}

public static Task<string> GetOrSetStringAsync(this IDistributedCache cache, string key,
Func<Task<string>> valueFactory, CancellationToken token = default)
{
return cache.GetOrSetStringAsync(key, valueFactory, new DistributedCacheEntryOptions(), token);
}

public static async Task<string> GetOrSetStringAsync(this IDistributedCache cache, string key,
Func<Task<string>> valueFactory, DistributedCacheEntryOptions options, CancellationToken token = default)
{
var cached = await cache.GetStringAsync(key, token);

if (cached != null)
{
return cached;
}

var value = await valueFactory();

await cache.SetStringAsync(key, value, options, token);

return value;
}
}
}
Loading

0 comments on commit e83ff77

Please sign in to comment.