Абстрагирование от асинхронных вызовов IMemoryCache в API - PullRequest
0 голосов
/ 25 сентября 2018

Я использовал действия, похожие на пример 1, для асинхронного кэширования результатов JSON для моего .NET Core API.MemoryCache является экземпляром IMemoryCache.

Пример 1 (работает должным образом):

[HttpGet]
public async Task<IActionResult> MyAction() =>
    Json(await MemoryCache.GetOrCreateAsync(
        "MyController_MyAction", 
        entry => myService.GetAllAsync()
    ));

Вызовы Json() и MemoryCache.GetOrCreate()дублируются во многих моих действиях.В моем реальном приложении есть еще более дублированные детали реализации, такие как установка значения AbsoluteExpirationRelativeToNow и возвращение NotFound() для нулей.Я хотел бы абстрагировать все это в общий метод, чтобы каждое действие передавало только свои уникальные детали вызову общего метода.

Для этого я извлек переменную для каждого издве переменные, которые в моих действиях.Например:

Пример 2 (кеш не обновляется и не извлекается из):

[HttpGet]
public async Task<IActionResult> MyAction()
{
    var task = myService.GetAllAsync();
    const string cacheKey = "MyController_MyAction";
    return Json(await MemoryCache.GetOrCreateAsync(cacheKey, entry => task));
}

Следующим шагом будет извлечение общего метода Get(), например:

Пример 3 (не работает, потому что пример 2 не работает):

[HttpGet]
public async Task<IActionResult> MyAction()
{
    var task = myService.GetAllAsync();
    const string cacheKey = "MyController_MyAction";
    return await Get(task, cacheKey);
}

protected async Task<IActionResult> Get(Task<T> task, string cacheKey =>
    return Json(await MemoryCache.GetOrCreateAsync(cacheKey, entry => task));

Пример 1 успешно извлекает последующие результаты из кэша.В примере 2, однако, обнаруживается null в кеше при последующих запросах и каждый раз заново извлекаются данные (что подтверждается инструкциями temp debug TryGetValue(), а также отслеживание базовых запросов SQL, попадающих в мою базу данных).

Для меня Пример 1 и Пример 2 должны быть идентичны.Однако, возможно, мое понимание async / await и Tasks отсутствует (очень вероятно).

Как я могу абстрагироваться от дублированных деталей реализации (таких как Json() и MemoryCache.GetOrCreate() вызовы) от моих действий, пока ещеуспешно обновлять и извлекать из IMemoryCache в асинхронном режиме?

Ответы [ 2 ]

0 голосов
/ 25 сентября 2018
var task = myService.GetAllAsync();

Это уже запустит метод GetAllAsync, поэтому вы предотвращаете ленивое поведение кеша памяти, когда он вызывает метод только тогда, когда ключ кеша недоступен.

Чтобы продолжать это делать, вам нужно будет сохранить фактическое выражение, которое создает значение, поэтому вам придется сделать следующее:

Func<MyObject> createValue = () => myService.GetAllAsync();
const string cacheKey = "MyController_MyAction";
return Json(await MemoryCache.GetOrCreateAsync(cacheKey, entry => createValue()));

Итак, абстрагируясь от этого, это то, что вы могли быв итоге:

public Task<IActionResult> MyAction()
    => GetCache("MyController_MyAction", () => myService.GetAllAsync());

Метод будет реализован следующим образом:

private async Task<IActionResult> GetCache<T>(string cacheKey, Func<Task<T>> createAction)
{
    var result = await MemoryCache.GetOrCreateAsync(cacheKey, entry => createAction());
    return Json(result);
}

Если ключ кеша всегда равен <ControllerName>_<ActionName>, вы можете даже сделать еще один шаг и автоматически сделать выводчто из звонка, используя CallerMemberNameAttribute:

private async Task<IActionResult> GetCache<T>(Func<Task<T>> createAction, [CallerMemberName] string actionName = null)
{
    var cacheKey = GetType().Name + "_" + actionName;
    var result = await MemoryCache.GetOrCreateAsync(cacheKey, entry => createAction());
    return Json(result);
}

Так что вы можете просто использовать его так:

public Task<IActionResult> MyAction()
    => GetCache(() => myService.GetAllAsync());
0 голосов
/ 25 сентября 2018

var task = myService.GetAllAsync(); не собирается создавать необходимую подпись Func.

public static System.Threading.Tasks.Task<TItem> GetOrCreateAsync<TItem> ( this Microsoft.Extensions.Caching.Memory.IMemoryCache cache, object key, Func<Microsoft.Extensions.Caching.Memory.ICacheEntry, System.Threading.Tasks.Task<TItem>> factory );
- CacheExtensions.GetOrCreateAsync MSDN

Вместо этого создайте функцию

Func<ICacheEntry,Task<TItem>> taskFunc = entry => myService.GetAllAsync();

, а затем используйте ее по мере необходимости

return Json(await MemoryCache.GetOrCreateAsync(cacheKey, taskFunc));
...