Pattern for caching data in Asp.Net Core + EF Core?

穿精又带淫゛_ 提交于 2019-12-04 10:46:21

Well, you can apply the decorator pattern. It's nothing .NET Core specific, just a common pattern.

public class MyModel
{
    public string SomeValue { get; set; }
}

public interface IMyRepository
{
    IEnumerable<MyModel> GetModel();
}

public class MyRepository : IMyRepository
{
    public IEnumerable<MyModel> GetModel()
    {
        return Set<MyModel>().FromSql("pr_GetMyModel");
    }
}

public class CachedMyRepositoryDecorator : IMyRepository
{
    private readonly IMyRepository repository;
    private readonly IMemoryCache cache;
    private const string MyModelCacheKey = "myModelCacheKey";
    private MemoryCacheEntryOptions cacheOptions;

    // alternatively use IDistributedCache if you use redis and multiple services
    public CachedMyRepositoryDecorator(IMyRepository repository, IMemoryCache cache)
    {
        this.repository = repository;
        this.cache = cache;

        // 1 day caching
        cacheOptions = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(relative: TimeSpan.FromDays(1));
    }

    public IEnumerable<MyModel> GetModel()
    {
        // Check cache
        var value = cache.Get<IEnumerable<MyModel>>("myModelCacheKey");
        if(value==null)
        {
            // Not found, get from DB
            value = repository.GetModel();

            // write it to the cache
            cache.Set("myModelCacheKey", value, cacheOptions);
        }

        return value;
    }
}

Since the ASP.NET Core DI doesn't support interceptors or decorators, your DI registration will become a bit more verbose. Alternatively use a 3rd party IoC container which supports decorator registrations.

services.AddScoped<MyRepository>();
services.AddScoped<IMyRepository, CachedMyRepositoryDecorator>(
    provider => new CachedMyRepositoryDecorator(
        provider.GetService<MyRepository>(),
        provider.GetService<IMemoryCache>()
    ));

This has the advantage that you have a clear separation of concerns and can easily disable the caching by changing the DI configuration to

services.AddScoped<IMyRepository,MyRepository>();

There is a sceond level caching extension that could do what you want. Its called EntityFrameworkCore.Cacheable (yes its created by me, because I had a similar problem).

Here a sample base on the extension usage:

var cacheableQuery = cacheableContext.Books
    .FromSql("pr_Getxxx")
    .Cacheable(TimeSpan.FromHours(24));

The Cacheable(... method call store the first result into memory cache and return the cached result for 24h if the same linq expression is called again.

you can use this third party package: https://github.com/VahidN/EFSecondLevelCache.Core

with AspNetCore MW & EfCore Extension (Cacheable) like this:

var posts = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable()
                   .ProjectTo<PostDto>(configuration: _mapper.ConfigurationProvider)
                   .ToList();

ProjectTo<>() is AutoMapper Extension.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!