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
24 changes: 24 additions & 0 deletions Cacheable.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{D1F536AC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cacheable.Tests", "tests\Cacheable.Tests\Cacheable.Tests.csproj", "{D4F95B27-30B5-4391-8322-A456A7EE4EA5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{FC7F7153-1BFC-476F-9AE6-C771F987D6B6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MediatR", "MediatR", "{A285DE98-56C9-4713-A033-EDC8BC65F110}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cacheable.Microsoft.Extensions.Caching.Abstractions", "src\Cacheable.Microsoft.Extensions.Caching.Abstractions\Cacheable.Microsoft.Extensions.Caching.Abstractions.csproj", "{F665B4DB-4416-41CE-AF82-F05CFC709067}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cacheable.MediatR", "src\Cacheable.MediatR\Cacheable.MediatR.csproj", "{126F7A77-6F66-4235-A8A4-76B416ED712E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiDotNetCore20", "samples\MediatR\WebApiDotNetCore20\WebApiDotNetCore20.csproj", "{1109466A-4366-44C6-908B-8A43053860B8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -29,12 +39,26 @@ Global
{D4F95B27-30B5-4391-8322-A456A7EE4EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4F95B27-30B5-4391-8322-A456A7EE4EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4F95B27-30B5-4391-8322-A456A7EE4EA5}.Release|Any CPU.Build.0 = Release|Any CPU
{F665B4DB-4416-41CE-AF82-F05CFC709067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F665B4DB-4416-41CE-AF82-F05CFC709067}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F665B4DB-4416-41CE-AF82-F05CFC709067}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F665B4DB-4416-41CE-AF82-F05CFC709067}.Release|Any CPU.Build.0 = Release|Any CPU
{126F7A77-6F66-4235-A8A4-76B416ED712E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{126F7A77-6F66-4235-A8A4-76B416ED712E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{126F7A77-6F66-4235-A8A4-76B416ED712E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{126F7A77-6F66-4235-A8A4-76B416ED712E}.Release|Any CPU.Build.0 = Release|Any CPU
{1109466A-4366-44C6-908B-8A43053860B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1109466A-4366-44C6-908B-8A43053860B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1109466A-4366-44C6-908B-8A43053860B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1109466A-4366-44C6-908B-8A43053860B8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D4F95B27-30B5-4391-8322-A456A7EE4EA5} = {8C6885A4-912E-4900-A809-EC9607DFA495}
{A285DE98-56C9-4713-A033-EDC8BC65F110} = {FC7F7153-1BFC-476F-9AE6-C771F987D6B6}
{1109466A-4366-44C6-908B-8A43053860B8} = {A285DE98-56C9-4713-A033-EDC8BC65F110}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E8200E84-166D-4FBF-90FD-9FAB13CE5191}
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
cacheable
====================

[![Build status](https://ci.appveyor.com/api/projects/status/w91o128dbwdwvj8a?svg=true)](https://ci.appveyor.com/project/neekgreen/cacheable)
[![NuGet](https://img.shields.io/nuget/v/cacheable.svg)](https://www.nuget.org/packages/cacheable)
[![NuGet](https://img.shields.io/nuget/dt/cacheable.svg)](https://www.nuget.org/packages/cacheable)
[![CodeFactor](https://www.codefactor.io/repository/github/neekgreen/cacheable/badge)](https://www.codefactor.io/repository/github/neekgreen/cacheable)

A set of extensions to provide caching support on MediatR based request handlers.

[![something](https://img.shields.io/badge/.netstandard-2.0-blue.svg)](https://img.shields.io/badge/.netstandard-1.3-blue.svg)

## Installing Cacheable

You should install [Cacheable with NuGet](https://www.nuget.org/packages/cacheable):

Install-Package Cacheable

This command will download and install Cacheable. Let me know if you have questions!

## Using Cacheable

Cacheable requires MediatR and your IoC container of choice. The decorator pattern is used to wrap the `IRequestHandler` and `IAsyncRequestHandler` classes with Cacheable implementations that will handle the caching.

### With StructureMap

```
For(typeof(IRequestHandler<,>)).DecorateAllWith(typeof(MemoryCacheRequestHandler<,>));
```

```
For(typeof(IAsyncRequestHandler<,>)).DecorateAllWith(typeof(MemoryCacheAsyncRequestHandler<,>));
```
Empty file modified build.sh
100644 → 100755
Empty file.
20 changes: 20 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/BenchmarkAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace WebApiDotNetCore20
{
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

public class BenchmarkAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var stopWatch = new Stopwatch();
stopWatch.Start();

await next();

stopWatch.Stop();
context.HttpContext.Response.Headers.Add("x-time-elapsed", stopWatch.Elapsed.ToString());
}
}
}
10 changes: 10 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Features/NoCache/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace WebApiDotNetCore20.Features.NoCache
{
using MediatR;
using Models;

public class Command : IRequest<Result>
{
public int Number { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace WebApiDotNetCore20.Features.NoCache
{
using System.Threading.Tasks;
using MediatR;
using Models;

public class CommandHandler : IAsyncRequestHandler<Command, Result>
{
private readonly IResultBuilder resultBuilder;

public CommandHandler(IResultBuilder resultBuilder)
{
this.resultBuilder = resultBuilder;
}

Task<Result> IAsyncRequestHandler<Command, Result>.Handle(Command message)
{
return Task.FromResult(resultBuilder.GetResult(message.Number));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace WebApiDotNetCore20.Features.NoCache
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MediatR;

[Route("api/no-cache")]
public class TestController : Controller
{
private readonly IMediator mediator;

public TestController(IMediator mediator)
{
this.mediator = mediator;
}

[Benchmark, HttpGet("{number}")]
public async Task<IActionResult> Get(int number)
{
var result =
await mediator.Send(new Command { Number = number });

return Ok(result);
}
}
}
21 changes: 21 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Features/WithCache/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace WebApiDotNetCore20.Features.WithCache
{
using System;
using Cacheable;
using MediatR;
using Models;

public class Command : IRequest<Result>, ICacheableRequest
{
public int Number { get; set; }

public bool IsCacheable { get; set; } = true;

public string GetCacheKey() { return "number:" + Number; }

public CacheEntryOptions GetCacheOptions()
{
return new CacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(5) };
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace WebApiDotNetCore20.Features.WithCache
{
using Cacheable;
using MediatR;
using Models;

public class CommandHandler : IRequestHandler<Command, Result>, ICacheableRequestHandler
{
private readonly IResultBuilder resultBuilder;

public CommandHandler(IResultBuilder resultBuilder)
{
this.resultBuilder = resultBuilder;
}

Result IRequestHandler<Command, Result>.Handle(Command message)
{
return resultBuilder.GetResult(message.Number);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace WebApiDotNetCore20.Features.WithCache
{
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MediatR;

[Route("api/with-cache")]
public class TestController : Controller
{
private readonly IMediator mediator;

public TestController(IMediator mediator)
{
this.mediator = mediator;
}

[Benchmark, HttpGet("{number}")]
public async Task<IActionResult> Get(int number, bool isCacheable)
{
var result =
await mediator.Send(new Command { Number = number, IsCacheable = isCacheable });

return Ok(result);
}
}
}
7 changes: 7 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Models/IResultBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace WebApiDotNetCore20.Models
{
public interface IResultBuilder
{
Result GetResult(int number);
}
}
11 changes: 11 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Models/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace WebApiDotNetCore20.Models
{
using System;

public class Result
{
public int Number { get; set; }
public string Text { get; set; }
public DateTimeOffset Created { get; } = DateTimeOffset.Now;
}
}
21 changes: 21 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Models/ResultBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace WebApiDotNetCore20.Models
{
using System.Threading;
using Humanizer;

public class ResultBuilder : IResultBuilder
{
public Result GetResult(int number)
{
Thread.Sleep(5000); //# this thing is expensive :(

var result = new Result
{
Number = number,
Text = number.ToWords(),
};

return result;
}
}
}
18 changes: 18 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace WebApiDotNetCore20
{
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
27 changes: 27 additions & 0 deletions samples/MediatR/WebApiDotNetCore20/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:64435/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebApiDotNetCore20": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:64436/"
}
}
}
Loading