简体   繁体   中英

Persistent in-memory concurrent dictionary in ASP.NET Core

In an ASP.NET Core application, I would like to have persistent, shared state that is analogous to:

ConcurrentDictionary<string, Job> Jobs;

Various components of the applications will access this shared state (request handling controllers, background tasks), but my main concern isn't concurrent access. What I'm curious about is whether there is a way to persist a global variable like this throughout the lifetime of my ASP.NET Core application.

Is there a place I can define this global Jobs variable where it will not be destroyed by the ASP.NET Core runtime? Perhaps leveraging MemoryCache in some way?

Using something like Redis would certainly work, but I'm curious if there is a robust in-memory/in-process solution for global shared state in ASP.NET Core.

You can wrap ConcurrentDictionary within a class and register it as singleton.

public class SharedJobs
{
    private readonly ConcurrentDictionary<string, Job> _jobs
        = new ConcurrentDictionary<string, Job>();

    public ConcurrentDictionary<string, Job> Jobs => _jobs;
}

In Startup.cs

services.AddSingleton<SharedJobs>();

Usage

public class Service
{
    private readonly SharedJobs _shared;

    public Service(SharedJobs shared) => _shared = shared;

    public void DoSomething()
    {
        var job = _shared.Jobs.GetOrAdd("Key", new Job("New Job when not found"));
    }
}

You can go further and hide the fact that you are using ConcurrentDictionary under the hood and expose only required functionality to the consumers.

public class SharedJobs
{
    private readonly ConcurrentDictionary<string, Job> _jobs
        = new ConcurrentDictionary<string, Job>();

    public Job Get(string key)
    {
        return _jobs.GetOrAdd(key, CreateNewJob());
    }

    private Job CreateNewJob() {}
}

Ask for a IMemoryCache in your controller's / service's constructor.

First add to your startup to register the cache services:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

...ask for it in a constructor...

private IMemoryCache _cache;

public HomeController(IMemoryCache memoryCache)
{
    _cache = memoryCache;
}

...and use it...

public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Save data in cache.
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

Read Microsoft's Cache in-memory in ASP.NET Core for more details. All of the above code comes from that page.

The memory cache provided here is a Singleton - a single instance of the cache will exist for the entire duration of the application. Be mindful though, as soon as the process shuts down, everything is cleared.


As for the concern "well what if my cache doesn't have the value at the moment I ask for it?"

Uh, welcome to multi-threaded code. This is just fact of life, cache-misses are a thing. It's going to be """"more""" reliable because the whole cycle is in memory, but you still need to take it into consideration.

You can use a persistent layer on top of IMemoryCache by using AcroFS tiny library. It will try to load data from memory first, then tries to load from disk.

If you have various projects in different locations you can set an absolute path for the cache folder.

// set cache
_memoryCache.Persistent().Set(key, jobs);

// get cache
var found = _memoryCache.Persistent().TryGetValue(cacheKey, out jobs);

// get or create
var jobs = await _memoryCache.Persistent().GetOrCreate(cacheKey, async entry => await loadJobsAsync());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM