ASP.NET Core - элемент обновления 1 кэшированного списка <Element>в IMemoryCache с использованием EF Core - PullRequest
0 голосов
/ 21 ноября 2019

Я создаю интернет-магазин, используя ASP.Net Core (Razor Pages) с EF Core. Чтобы ускорить вычисления, я кеширую все продукты (~ 100.000). Информация поступает из нашей базы данных.

Модели

class ProductInformation{
    // some information about how to display a product
    public string productnumber {get; set;}
    public Product product {get; set;}
}

class Product{
    public string productnumber {get; set;}
    public decimal price {get; set;}
    public Category category {get; set}
    // and a lot more...
}

В моем DBContext я определяю, что каждая PoductInformation имеет точный продукт.

метод кэширования

private readonly IMemoryCache _cache; // is set in the constructor
private readonly DBContext _dbContext; // is set in the constructor
public List<ProductInformation> GetProductInformationList(){
    List<ProductInformation> = _cache.GetOrCreate("ProductInformations", entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24);
        List<ProductInformation> list =_dbContext.ProductInformation.ToList();
        // do a lot of calculations on the list-elements
        return list;
    });
}

Весь метод кэширования занимает 100-120 секунд. Теперь другая система обновляет мою цену в БД для одного продукта, и, конечно, я хочу показать правильную цену этого продукта в моем интернет-магазине. Давайте предположим, что я хочу проверять цену каждые 15 минут.

Решения - пробовали, но не удовлетворяли

Метод 1

Я могу установить кэшированиеВесь список до 15 минут, это будет работать, но это не то, что я хочу. Обновление всего кэша происходит медленно и не нужно. 99,9% данных не изменились.

Метод 2

Внутри моей модели ProductInformation я создаю метод, который обновляет Продукт:

class ProductInformation{
    // some information about how to display a product
    public string productnumber {get; set;}
    public Product product {get; set;}

    pubic DateTime ProductTimeStamp {get; set;}
    public UpdateProduct(){
        // If ProductTimeStamp is more than 15 minutes in the past
        // get Product from the DB
        // and update the timestamp
    }
}

Везде, где я отображаю ProductInformation, я звоню UpdateProduct(). Затем я проверяю только «старые» цены из отображаемых продуктов. Это гораздо эффективнее, чем пересчитать весь кеш. Но теперь мне нужно соединение с БД внутри моей ProductInformation (которая кешируется). Я не могу заставить это работать.

Метод 3

Поскольку проблема в методе 2 заключается в том, что у меня нет подключения к БД, я могу взятьUpdateProduct() метод вне модели и поместите его в мой репозиторий, где у меня есть соединение с БД. Везде, где я отображаю ProdctInformation, мне нужно вызывать что-то вроде _proudctRepository.UpdateProduct(ref ProductInformation);. Этот метод выглядит следующим образом:

public void UpdateProduct(ref ProductInformation pi){
    pi.Product = _dbContext.Product.Where(p => p.productnumber == pi.productnumber);
   // Of course I also need to do the calculations from the caching-method in GetProductInformationList()
}

Но это не так. Entity Framework организовал для меня связь между ProductInformation и Product, возможно ли тогда переопределить Product таким образом? Я думаю, что это не тот путь.

Вопрос

Я думаю, что многие люди используют IMemoryCaching для одинаковых ситуаций (когда вы хотите обновить только один элемент в кэшированном Списке или просто некоторыеэлементы (цена / акции / ...) элемента кэшированного списка). Как мы можем справиться с этим?

1 Ответ

1 голос
/ 21 ноября 2019

1) Добавьте столбец LastChanged (datetime [offset]) в вашу базу данных Product и попросите "другую систему" обновить его, когда он обновит вашу цену. Благодаря этому вы можете легко сохранять Max(LastChanged) в своем кэше и запрашивать только измененные продукты, сокращая время и размер обновлений, а также делать это чаще.

2) Добавить AsNoTracking при помещении данных в кеш. Вы не будете обновлять их обратно в БД (я полагаю), поэтому no-tracking немного ускорит все. Кроме того, это не вызовет беспокойства по поводу отношений ProductInformation - Product EF, поскольку EF все равно не будет их отслеживать, а pi.Product будет обычным свойством удержания объекта без какой-либо скрытой магии.

3) Не стоит проверять обновления цен на каждой странице рендера. Обновления должны запускаться в другом потоке / фоне. Установите некоторую фоновую задачу, которая будет проверять обновленные цены и загружать обновленные объекты в кэш - с помощью (1) вы можете запускать обновления каждые 5 минут или меньше. Проверьте здесь DbContext для фоновых задач через Dependency Injection для получения DbContext.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...