Skip to content
Open
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
5 changes: 3 additions & 2 deletions sample/WebApi.OutputCache.V2.Demo/TeamsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using WebApi.OutputCache.V2.TimeAttributes;

Expand Down Expand Up @@ -37,7 +38,7 @@ public void Post(Team value)
Teams.Add(value);
}

public void Put(int id, Team value)
public async Task Put(int id, Team value)
{
if (!ModelState.IsValid) throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));

Expand All @@ -48,7 +49,7 @@ public void Put(int id, Team value)
team.Name = value.Name;

var cache = Configuration.CacheOutputConfiguration().GetCacheOutputProvider(Request);
cache.RemoveStartsWith(Configuration.CacheOutputConfiguration().MakeBaseCachekey((TeamsController t) => t.GetById(0)));
await cache.RemoveStartsWithAsync(Configuration.CacheOutputConfiguration().MakeBaseCachekey((TeamsController t) => t.GetById(0)));
}

public void Delete(int id)
Expand Down
7 changes: 4 additions & 3 deletions src/WebApi.OutputCache.Core/Cache/CacheExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
using System;
using System.Threading.Tasks;

namespace WebApi.OutputCache.Core.Cache
{
public static class CacheExtensions
{
public static T GetCachedResult<T>(this IApiOutputCache cache, string key, DateTimeOffset expiry, Func<T> resultGetter, bool bypassCache = true) where T : class
public static async Task<T> GetCachedResultAsync<T>(this IApiOutputCache cache, string key, DateTimeOffset expiry, Func<T> resultGetter, bool bypassCache = true) where T : class
{
var result = cache.Get<T>(key);
var result = await cache.GetAsync<T>(key);

if (result == null || bypassCache)
{
result = resultGetter();
if (result != null) cache.Add(key, result, expiry);
if (result != null) await cache.AddAsync(key, result, expiry);
}

return result;
Expand Down
16 changes: 7 additions & 9 deletions src/WebApi.OutputCache.Core/Cache/IApiOutputCache.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace WebApi.OutputCache.Core.Cache
{
public interface IApiOutputCache
{
void RemoveStartsWith(string key);
Task RemoveStartsWithAsync(string key);

T Get<T>(string key) where T : class;
Task<T> GetAsync<T>(string key) where T : class;

[Obsolete("Use Get<T> instead")]
object Get(string key);
Task RemoveAsync(string key);

void Remove(string key);
Task<bool> ContainsAsync(string key);

bool Contains(string key);
Task AddAsync(string key, object value, DateTimeOffset expiration, string dependsOnKey = null);

void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null);

IEnumerable<string> AllKeys { get; }
Task<IEnumerable<string>> AllKeysAsync { get; }
}
}
49 changes: 36 additions & 13 deletions src/WebApi.OutputCache.Core/Cache/MemoryCacheDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Threading.Tasks;

namespace WebApi.OutputCache.Core.Cache
{
public class MemoryCacheDefault : IApiOutputCache
{
private static readonly MemoryCache Cache = MemoryCache.Default;

public virtual void RemoveStartsWith(string key)
private static void RemoveStartsWith(string key)
{
lock (Cache)
{
Cache.Remove(key);
}
}

public virtual T Get<T>(string key) where T : class
private static T Get<T>(string key) where T : class
{
var o = Cache.Get(key) as T;
return o;
}

[Obsolete("Use Get<T> instead")]
public virtual object Get(string key)
{
return Cache.Get(key);
}

public virtual void Remove(string key)
private static void Remove(string key)
{
lock (Cache)
{
Cache.Remove(key);
}
}

public virtual bool Contains(string key)
private static bool Contains(string key)
{
return Cache.Contains(key);
}

public virtual void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null)
private static void Add(string key, object o, DateTimeOffset expiration, string dependsOnKey = null)
{
var cachePolicy = new CacheItemPolicy
{
Expand All @@ -61,12 +56,40 @@ public virtual void Add(string key, object o, DateTimeOffset expiration, string
}
}

public virtual IEnumerable<string> AllKeys
public virtual Task<IEnumerable<string>> AllKeysAsync
{
get
{
return Cache.Select(x => x.Key);
return Task.FromResult(Cache.Select(x => x.Key));
}
}

public virtual Task RemoveStartsWithAsync(string key)
{
RemoveStartsWith(key);
return Task.FromResult(0);
}

public virtual Task<T> GetAsync<T>(string key) where T : class
{
return Task.FromResult(Get<T>(key));
}

public virtual Task RemoveAsync(string key)
{
Remove(key);
return Task.FromResult(0);
}

public virtual Task<bool> ContainsAsync(string key)
{
return Task.FromResult(Contains(key));
}

public virtual Task AddAsync(string key, object value, DateTimeOffset expiration, string dependsOnKey = null)
{
Add(key, value, expiration, dependsOnKey);
return Task.FromResult(0);
}
}
}
6 changes: 4 additions & 2 deletions src/WebApi.OutputCache.Core/WebApi.OutputCache.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand All @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WebApi.OutputCache.Core</RootNamespace>
<AssemblyName>WebApi.OutputCache.Core</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
Expand All @@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
Expand All @@ -29,6 +30,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
Expand All @@ -15,7 +17,7 @@ public sealed class AutoInvalidateCacheOutputAttribute : BaseCacheAttribute
{
public bool TryMatchType { get; set; }

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
if (actionExecutedContext.Response != null && !actionExecutedContext.Response.IsSuccessStatusCode) return;
if (actionExecutedContext.ActionContext.Request.Method != HttpMethod.Post &&
Expand All @@ -33,9 +35,9 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo
foreach (var action in actions)
{
var key = config.CacheOutputConfiguration().MakeBaseCachekey(controller.ControllerType.FullName, action);
if (WebApiCache.Contains(key))
if (await WebApiCache.ContainsAsync(key))
{
WebApiCache.RemoveStartsWith(key);
await WebApiCache.RemoveStartsWithAsync(key);
}
}
}
Expand Down
30 changes: 14 additions & 16 deletions src/WebApi.OutputCache.V2/CacheOutputAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ protected virtual MediaTypeHeaderValue GetExpectedMediaType(HttpConfiguration co
return responseMediaType;
}

public override void OnActionExecuting(HttpActionContext actionContext)
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
if (actionContext == null) throw new ArgumentNullException("actionContext");

Expand All @@ -183,11 +183,11 @@ public override void OnActionExecuting(HttpActionContext actionContext)
actionContext.Request.Properties[CurrentRequestMediaType] = responseMediaType;
var cachekey = cacheKeyGenerator.MakeCacheKey(actionContext, responseMediaType, ExcludeQueryStringFromCacheKey);

if (!_webApiCache.Contains(cachekey)) return;
if (await _webApiCache.ContainsAsync(cachekey) == false) return;

if (actionContext.Request.Headers.IfNoneMatch != null)
{
var etag = _webApiCache.Get<string>(cachekey + Constants.EtagKey);
var etag = await _webApiCache.GetAsync<string>(cachekey + Constants.EtagKey);
if (etag != null)
{
if (actionContext.Request.Headers.IfNoneMatch.Any(x => x.Tag == etag))
Expand All @@ -203,11 +203,11 @@ public override void OnActionExecuting(HttpActionContext actionContext)
}
}

var val = _webApiCache.Get<byte[]>(cachekey);
var val = await _webApiCache.GetAsync<byte[]>(cachekey);
if (val == null) return;

var contenttype = _webApiCache.Get<MediaTypeHeaderValue>(cachekey + Constants.ContentTypeKey) ?? responseMediaType;
var contentGeneration = _webApiCache.Get<string>(cachekey + Constants.GenerationTimestampKey);
var contenttype = await _webApiCache.GetAsync<MediaTypeHeaderValue>(cachekey + Constants.ContentTypeKey) ?? responseMediaType;
var contentGeneration = await _webApiCache.GetAsync<string>(cachekey + Constants.GenerationTimestampKey);

DateTimeOffset? contentGenerationTimestamp = null;
if (contentGeneration != null)
Expand All @@ -222,7 +222,7 @@ public override void OnActionExecuting(HttpActionContext actionContext)
actionContext.Response.Content = new ByteArrayContent(val);

actionContext.Response.Content.Headers.ContentType = contenttype;
var responseEtag = _webApiCache.Get<string>(cachekey + Constants.EtagKey);
var responseEtag = await _webApiCache.GetAsync<string>(cachekey + Constants.EtagKey);
if (responseEtag != null) SetEtag(actionContext.Response, responseEtag);

var cacheTime = CacheTimeQuery.Execute(DateTime.Now);
Expand All @@ -246,7 +246,7 @@ public override async Task OnActionExecutedAsync(HttpActionExecutedContext actio
var responseMediaType = actionExecutedContext.Request.Properties[CurrentRequestMediaType] as MediaTypeHeaderValue ?? GetExpectedMediaType(httpConfig, actionExecutedContext.ActionContext);
var cachekey = cacheKeyGenerator.MakeCacheKey(actionExecutedContext.ActionContext, responseMediaType, ExcludeQueryStringFromCacheKey);

if (!string.IsNullOrWhiteSpace(cachekey) && !(_webApiCache.Contains(cachekey)))
if (!string.IsNullOrWhiteSpace(cachekey) && !(await _webApiCache.ContainsAsync(cachekey)))
{
SetEtag(actionExecutedContext.Response, CreateEtag(actionExecutedContext, cachekey, cacheTime));

Expand All @@ -262,21 +262,19 @@ public override async Task OnActionExecutedAsync(HttpActionExecutedContext actio

responseContent.Headers.Remove("Content-Length");

_webApiCache.Add(baseKey, string.Empty, cacheTime.AbsoluteExpiration);
_webApiCache.Add(cachekey, content, cacheTime.AbsoluteExpiration, baseKey);

await _webApiCache.AddAsync(baseKey, string.Empty, cacheTime.AbsoluteExpiration);
await _webApiCache.AddAsync(cachekey, content, cacheTime.AbsoluteExpiration, baseKey);

_webApiCache.Add(cachekey + Constants.ContentTypeKey,
await _webApiCache.AddAsync(cachekey + Constants.ContentTypeKey,
contentType,
cacheTime.AbsoluteExpiration, baseKey);


_webApiCache.Add(cachekey + Constants.EtagKey,

await _webApiCache.AddAsync(cachekey + Constants.EtagKey,
etag,
cacheTime.AbsoluteExpiration, baseKey);


_webApiCache.Add(cachekey + Constants.GenerationTimestampKey,
await _webApiCache.AddAsync(cachekey + Constants.GenerationTimestampKey,
actionExecutionTimestamp.ToString(),
cacheTime.AbsoluteExpiration, baseKey);
}
Expand Down
8 changes: 5 additions & 3 deletions src/WebApi.OutputCache.V2/InvalidateCacheOutputAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;

namespace WebApi.OutputCache.V2
Expand All @@ -21,7 +23,7 @@ public InvalidateCacheOutputAttribute(string methodName, Type type = null)
_methodName = methodName;
}

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
if (actionExecutedContext.Response != null && !actionExecutedContext.Response.IsSuccessStatusCode) return;
_controller = _controller ?? actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName;
Expand All @@ -30,9 +32,9 @@ public override void OnActionExecuted(HttpActionExecutedContext actionExecutedCo
EnsureCache(config, actionExecutedContext.Request);

var key = actionExecutedContext.Request.GetConfiguration().CacheOutputConfiguration().MakeBaseCachekey(_controller, _methodName);
if (WebApiCache.Contains(key))
if (await WebApiCache.ContainsAsync(key))
{
WebApiCache.RemoveStartsWith(key);
await WebApiCache.RemoveStartsWithAsync(key);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/WebApi.OutputCache.V2/WebApi.OutputCache.V2.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down
Loading