Skip to content

Commit

Permalink
Merge pull request #33 from reisenberger/v300
Browse files Browse the repository at this point in the history
Version 3.0.0: For compatibility with (forthcoming) Polly v7.0.0
  • Loading branch information
reisenberger authored Feb 9, 2019
2 parents 84bbf5b + 46edf31 commit a7d6abf
Show file tree
Hide file tree
Showing 19 changed files with 376 additions and 67 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# Polly.Caching.MemoryCache change log
# Polly.Caching.Memory change log

## 3.0.0
- Allow caching of `default(TResult)`
- Compatible with Polly >= v7

## 2.0.2
- No functional changes
- Indicate compatibility with Polly < v7

## 2.0.2
- No functional changes
Expand Down
2 changes: 1 addition & 1 deletion GitVersionConfig.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
next-version: 2.0.2
next-version: 3.0.0
50 changes: 27 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,70 +27,73 @@ Polly.Caching.MemoryCache <v2.0 supports .NET4.0, .NET4.5 and .NetStandard 1.

## Versions and Dependencies

Polly.Caching.Memory >=v2.0.2 and <v3 requires:
Polly.Caching.Memory >=v3.0 requires:

+ [Polly](https://nuget.org/packages/polly) >= v7.0.0.
+ [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/) v2.0.2 or above (or v1.1.2, for NetStandard 1.3).

Polly.Caching.Memory >=v2.0.1 and <v3 requires:

+ [Polly](https://nuget.org/packages/polly) >= v6.1.1 and <v7.
+ [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/) v2.0.2 or above (or v1.1.2, for NetStandard 1.3).

Polly.Caching.Memory >= v2.0.1 requires:
Polly.Caching.Memory v2.0.0 requires:

+ [Polly](https://nuget.org/packages/polly) >= v6.0.1 and <=v6.1.0.
+ [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/) v2.0.2 or above (or v1.1.2, for NetStandard 1.3).

Polly.Caching.MemoryCache <v1.* requires:
Polly.Caching.MemoryCache v1.* requires:

+ [Polly](https://nuget.org/packages/polly) >=v5.9.0 and <v6.
+ [Microsoft.Extensions.Caching.Memory](https://www.nuget.org/packages/Microsoft.Extensions.Caching.Memory/) v1.1.2, for NetStandard 1.3.

# How to use the Polly.Caching.Memory plugin

```csharp
// (1a): Create a MemoryCacheProvider instance in the .NET Framework, using the Polly.Caching.Memory nuget package.
// (full namespaces and types only shown here for disambiguation)
Polly.Caching.Memory.MemoryCacheProvider memoryCacheProvider
= new Polly.Caching.Memory.MemoryCacheProvider(System.Runtime.Caching.MemoryCache.Default);
### Example: Direct creation of CachePolicy (no DI)

// Or (1b): Create a MemoryCacheProvider instance in .NET Core / .NET Standard.
// (full namespaces and types only shown here for disambiguation)
// NB Only if you want to create your own Microsoft.Extensions.Caching.Memory.MemoryCache instance:
```csharp
// This approach creates a CachePolicy directly, with its own Microsoft.Extensions.Caching.Memory.MemoryCache instance:
Microsoft.Extensions.Caching.Memory.IMemoryCache memoryCache
= new Microsoft.Extensions.Caching.Memory.MemoryCache(new Microsoft.Extensions.Caching.Memory.MemoryCacheOptions());
Polly.Caching.Memory.MemoryCacheProvider memoryCacheProvider
= new Polly.Caching.Memory.MemoryCacheProvider(memoryCache);

// (2) Create a Polly cache policy using that Polly.Caching.Memory.MemoryCacheProvider instance.
// Create a Polly cache policy using that Polly.Caching.Memory.MemoryCacheProvider instance.
var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5));
```

### Example: Configure CachePolicy via MemoryCacheProvider in StartUp, for DI

```csharp
// (We pass a whole PolicyRegistry by dependency injection rather than the individual policy,
// on the assumption the app will probably use multiple policies.)
// Or (1c): Configure by dependency injection within ASP.NET Core
// See https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory
// and https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection#registering-your-own-services
// (In this example we choose to pass a whole PolicyRegistry by dependency injection rather than the individual policy, on the assumption the webapp will probably use multiple policies across the app.)
// For example:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddSingleton<Polly.Caching.IAsyncCacheProvider, Polly.Caching.Memory.MemoryCacheProvider>();

services.AddSingleton<Polly.Registry.IPolicyRegistry<string>, Polly.Registry.PolicyRegistry>((serviceProvider) =>
services.AddSingleton<Polly.Registry.IReadOnlyPolicyRegistry<string>, Polly.Registry.PolicyRegistry>((serviceProvider) =>
{
PolicyRegistry registry = new PolicyRegistry();
registry.Add("myCachePolicy", Policy.CacheAsync<HttpResponseMessage>(serviceProvider.GetRequiredService<IAsyncCacheProvider>().AsyncFor<HttpResponseMessage>(), TimeSpan.FromMinutes(5)));
registry.Add("myCachePolicy",
Policy.CacheAsync<HttpResponseMessage>(
serviceProvider
.GetRequiredService<IAsyncCacheProvider>()
.AsyncFor<HttpResponseMessage>(),
TimeSpan.FromMinutes(5)));
return registry;
});

// ...
}
}

// In a controller, inject the policyRegistry and retrieve the policy:
// At the point of use, inject the policyRegistry and retrieve the policy:
// (magic string "myCachePolicy" only hard-coded here to keep the example simple)
public MyController(IPolicyRegistry<string> policyRegistry)
public MyController(IReadOnlyPolicyRegistry<string> policyRegistry)
{
var _cachePolicy = policyRegistry.Get<IAsyncPolicy<HttpResponseMessage>>("myCachePolicy");
// ...
Expand All @@ -117,6 +120,7 @@ For details of changes by release see the [change log](CHANGELOG.md).
* [@seanfarrow](https://github.com/seanfarrow) and [@reisenberger](https://github.com/reisenberger) - Initial caching architecture in the main Polly repo
* [@kesmy](https://github.com/kesmy) - original structuring of the build for msbuild15, in the main Polly repo
* [@seanfarrow](https://github.com/seanfarrow) - v2.0 update to Signed packages only to correspond with Polly v6.0.1
* [@reisenberger](https://github.com/reisenberger) - Update to Polly v7.0.0


# Instructions for Contributing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
<PackageReference Include="Polly" Version="6.1.1" />
<PackageReference Include="Polly" Version="7.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>
<Import Project="..\Polly.Caching.Memory.Shared\Polly.Caching.Memory.Shared.projitems" Label="Shared" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle("Polly.Caching.Memory")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.2.0")]
[assembly: AssemblyInformationalVersion("2.0.2.0")]
[assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0.0")]
[assembly: CLSCompliant(false)] // Because Microsoft.Extensions.Caching.Memory.MemoryCache, on which Polly.Caching.MemoryCache.NetStandard13 depends, is not CLSCompliant.

[assembly: InternalsVisibleTo("Polly.Caching.Memory.NetStandard13.Specs")]
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.2" />
<PackageReference Include="Polly" Version="6.1.1" />
<PackageReference Include="Polly" Version="7.0.0" />
</ItemGroup>
<Import Project="..\Polly.Caching.Memory.Shared\Polly.Caching.Memory.Shared.projitems" Label="Shared" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle("Polly.Caching.Memory")]
[assembly: AssemblyVersion("2.0.0.0")]
[assembly: AssemblyFileVersion("2.0.2.0")]
[assembly: AssemblyInformationalVersion("2.0.2.0")]
[assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyInformationalVersion("3.0.0.0")]
[assembly: CLSCompliant(false)] // Because Microsoft.Extensions.Caching.Memory.MemoryCache, on which Polly.Caching.MemoryCache.NetStandard13 depends, is not CLSCompliant.

[assembly: InternalsVisibleTo("Polly.Caching.Memory.NetStandard20.Specs")]
40 changes: 20 additions & 20 deletions src/Polly.Caching.Memory.Shared/MemoryCacheProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,23 @@ public class MemoryCacheProvider : ISyncCacheProvider, IAsyncCacheProvider
/// <param name="memoryCache">The memory cache instance in which to store cached items.</param>
public MemoryCacheProvider(IMemoryCache memoryCache)
{
if (memoryCache == null) throw new ArgumentNullException(nameof(memoryCache));
_cache = memoryCache;
_cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
}

/// <summary>
/// Gets a value from cache.
/// </summary>
/// <param name="key">The cache key.</param>
/// <returns>The value from cache; or null, if none was found.</returns>
public object Get(String key)
/// <returns>
/// A tuple whose first element is a value indicating whether the key was found in the cache,
/// and whose second element is the value from the cache (null if not found).
/// </returns>
public (bool, object) TryGet(string key)
{
object value;
if (_cache.TryGetValue(key, out value))
{
return value;
}
return null;
bool cacheHit = _cache.TryGetValue(key, out var value);
return (cacheHit, value);
}

/// <summary>
/// Puts the specified value in the cache.
/// </summary>
Expand All @@ -59,7 +57,7 @@ public void Put(string key, object value, Ttl ttl)
{
options.AbsoluteExpiration = DateTimeOffset.MaxValue;
}
else
else
{
options.AbsoluteExpirationRelativeToNow = ttl.Timespan < remaining ? ttl.Timespan : remaining;
}
Expand All @@ -69,17 +67,20 @@ public void Put(string key, object value, Ttl ttl)
}

/// <summary>
/// Gets a value from the memory cache as part of an asynchronous execution. <para><remarks>The implementation is synchronous as there is no advantage to an asynchronous implementation for an in-memory cache.</remarks></para>
/// Gets a value from the cache asynchronously.
/// <para><remarks>The implementation is synchronous as there is no advantage to an asynchronous implementation for an in-memory cache.</remarks></para>
/// </summary>
/// <param name="key">The cache key.</param>
/// <param name="cancellationToken">The cancellation token. </param>
/// <param name="continueOnCapturedContext">Whether async calls should continue on a captured synchronization context. <para><remarks>For <see cref="MemoryCacheProvider"/>, this parameter is irrelevant and is ignored, as the implementation is synchronous.</remarks></para></param>
/// <returns>A <see cref="Task{TResult}" /> promising as Result the value from cache; or null, if none was found.</returns>
public Task<object> GetAsync(string key, CancellationToken cancellationToken, bool continueOnCapturedContext)
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="continueOnCapturedContext">Whether async calls should continue on a captured synchronization context. <para><remarks>Note: if the underlying cache's async API does not support controlling whether to continue on a captured context, async Policy executions with continueOnCapturedContext == true cannot be guaranteed to remain on the captured context.</remarks></para></param>
/// <returns>
/// A <see cref="Task{TResult}" /> promising as Result a tuple whose first element is a value indicating whether
/// the key was found in the cache, and whose second element is the value from the cache (null if not found).
/// </returns>
public Task<(bool, object)> TryGetAsync(string key, CancellationToken cancellationToken, bool continueOnCapturedContext)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(Get(key));
// (With C#7.0, a ValueTask<> approach would be preferred, but some of our tfms do not support that. TO DO: Implement it, with preprocessor if/endif directives, for NetStandard)
return Task.FromResult(TryGet(key));
}

/// <summary>
Expand All @@ -97,7 +98,6 @@ public Task PutAsync(string key, object value, Ttl ttl, CancellationToken cancel
cancellationToken.ThrowIfCancellationRequested();
Put(key, value, ttl);
return Task.CompletedTask;
// (With C#7.0, a ValueTask<> approach would be preferred, but some of our tfms do not support that. TO DO: Implement it, with preprocessor if/endif directives, for NetStandard)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace Polly.Caching.Memory.Specs.Integration
{
public abstract class CacheRoundTripSpecsBase
{
protected CacheRoundTripSpecsBase(ICachePolicyFactory cachePolicyFactory)
{
CachePolicyFactory = cachePolicyFactory;
}

protected ICachePolicyFactory CachePolicyFactory { get; }

protected const string OperationKey = "SomeOperationKey";

public abstract Task Should_roundtrip_this_variant_of<TResult>(TResult testValue);

[Theory]
[MemberData(nameof(SampleClassData))]
public async Task Should_roundtrip_all_variants_of_reference_type(SampleClass testValue)
{
await Should_roundtrip_this_variant_of<SampleClass>(testValue);
}

[Theory]
[MemberData(nameof(SampleStringData))]
public async Task Should_roundtrip_all_variants_of_string(String testValue)
{
await Should_roundtrip_this_variant_of<String>(testValue);
}

[Theory]
[MemberData(nameof(SampleNumericData))]
public async Task Should_roundtrip_all_variants_of_numeric(int testValue)
{
await Should_roundtrip_this_variant_of<int>(testValue);
}

[Theory]
[MemberData(nameof(SampleEnumData))]
public async Task Should_roundtrip_all_variants_of_enum(SampleEnum testValue)
{
await Should_roundtrip_this_variant_of<SampleEnum>(testValue);
}

[Theory]
[MemberData(nameof(SampleBoolData))]
public async Task Should_roundtrip_all_variants_of_bool(bool testValue)
{
await Should_roundtrip_this_variant_of<bool>(testValue);
}

[Theory]
[MemberData(nameof(SampleNullableBoolData))]
public async Task Should_roundtrip_all_variants_of_nullable_bool(bool? testValue)
{
await Should_roundtrip_this_variant_of<bool?>(testValue);
}

public static TheoryData<SampleClass> SampleClassData =>
new TheoryData<SampleClass>
{
new SampleClass(),
new SampleClass()
{
StringProperty = "<html></html>",
IntProperty = 1
},
(SampleClass)null,
default(SampleClass)
};

public static TheoryData<String> SampleStringData =>
new TheoryData<String>
{
"some string",
"",
null,
default(string),
"null"
};

public static TheoryData<int> SampleNumericData =>
new TheoryData<int>
{
-1,
0,
1,
default(int)
};

public static TheoryData<SampleEnum> SampleEnumData =>
new TheoryData<SampleEnum>
{
SampleEnum.FirstValue,
SampleEnum.SecondValue,
default(SampleEnum),
};

public static TheoryData<bool> SampleBoolData =>
new TheoryData<bool>
{
true,
false,
default(bool),
};

public static TheoryData<bool?> SampleNullableBoolData =>
new TheoryData<bool?>
{
true,
false,
null,
default(bool?),
};

public class SampleClass
{
public string StringProperty { get; set; }
public int IntProperty { get; set; }
}

public enum SampleEnum
{
FirstValue,
SecondValue,
}
}
}
Loading

0 comments on commit a7d6abf

Please sign in to comment.