One way to improve application performance is to use a cache of some sort. This could include caching websites on a server closer to the user's location, caching regularly used data for faster access, and more. For large applications you may be able to utilize something like Redis or Azure, but other times a simple in-memory cache could work as well.

In one project I'm working on, I'm using an authorization policy that only passes when a user has a certain role attached to their token. The token only provides the role's ID, so I need to read the role from the database. Due to the fact that this could happen frequently, and roles won't be changing often, if at all, I thought cache would be a good way to speed up the authorization checks.

.Net's MemoryCache class is a nice, simple to use solution in this case.

Basic Use

In it's most simple form, you can install the Microsoft.Extensions.Caching.Memory nuget. From there, you can add the following to your startup.cs


That provides an IMemoryCache that you can then utilize in areas where caching is required. In order to do so, you can make use of dependency injection and then call TryGetValue(), Set(), etc.

public class MyServiceOrController
  private readonly IMemoryCache Cache;

  public MyServiceOrController(IMemoryCache cache)
    Cache = cache;

  public object GetItem(string key)
    Cache.TryGetValue(key, out object value);
    if(value == null)
      // Get the value from somewhere
      value = GetValue();
      Cache.Set(key, value, new MemoryCacheOptions());

    return value;

While this is nice, there are a few issues that could pop up.

  1. An expiration may not be set on an item, meaning it could become stale.
  2. There's the possibility that the cache continues to grow unchecked, eventually consuming all of the available memory.
  3. Each place that uses the IMemoryCache is responsible for knowing what MemoryCacheOptions should be used.

To address these, I decided to make a cache service that uses a MemoryCache.

Custom implementation

The idea behind the custom implementation is to eliminate the potential issues from above. If you'd like to see the full code, you can take a look at my repo on GitHub


First, I created an interface that can be utilized and provides some standard functions.

public interface ICacheService
  public object GetItem(string key);
  public void SetItem(string key, object value);
  public void SetItem(string key, object value, TimeSpan validFor);
  public object GetAndSetItem(string key, Func<object> setter);
  public object GetAndSetItem(string key, Func<object> setter, TimeSpan validFor);

The functions are pretty self-explanatory. I decided to make two SetItem and GetAndSetItem functions though - one that could set a default expiration and another that would allow you to override the default expiration.


Next, the implmentation uses a MemoryCache behind the scenes.

public class FarmCraftCache : ICacheService
  private readonly MemoryCache _cache;
  private readonly MemoryCacheEntryOptions _defaultEntryOptions;

  public FarmCraftCache(IOptions<CacheSettings> settings)
    _cache = new MemoryCache(new MemoryCacheOptions
      SizeLimit = 1024

    _defaultEntryOptions = new MemoryCacheEntryOptions()

Setting the SizeLimit to 1024 means that I can only have 1024 items in the cache, because my _defaulEntryOptions says that any item added will take 1 space.

The default options also set an expiration, so I don't have to worry about any of that when using the FarmCraftCache.

The rest of the functions defined in the interface are just wrapping the actual MemoryCache functions with the default MemoryCacheEntryOptions specified in the constructor.


Once the custom cache service has been created, we merely register it where needed, and access it through dependency injection.

// API Startup
builder.Services.AddSingleton<ICacheService, FarmCraftCache>();

// API Policy Handler
public class AdminHandler : AuthorizationHandler<AdminRequirement>
  private readonly ICacheService _cache;

  public AdminHandler(ICacheService cache)
    _cache = cache;

  protected override Task HandleRequirementAsync(
    AuthorizationHandlerContext context,
    AdminRequirement requirement

    Role? adminRole = _cache.GetAndSetItem("admin_role", GetAdminRole) as Role;


  private Role GetAdminRole()
    FarmCraftActorResponse? response = _root.Ask(
      new AskForRole(null, "admin"),
    ).Result as FarmCraftActorResponse;

    return response != null && response.Data as Role != null
        ? (Role)response.Data
        : new Role();

First, the _cache.GetAndSetItem tries to pull the value from cache, if available. If not, it will then use the provided getter, GetAdminRole to retrieve the value, and then store the result in cache.

GetAdminRole is using an internal actor system to ask for the role with a name of "admin"

That's all there is to it, and so far it seems to work fairly well. Let me know any thoughts or suggestions in the comments!