رأيي في ذاكرة التخزين المؤقت في الذاكرة

· 3 دقيقة قراءة

لقد كنت أعمل على بعض الأشياء التي تعاملت مع كمية كبيرة من البيانات. أثناء القيام بذلك، أدركت أن جزءًا كبيرًا منه لا يتغير أبدًا، أو على الأقل لبعض الوقت المحدد، لا يتغير.

لذلك اعتقدت أنه سيكون من المفيد إنشاء مستودع ذاكرة تخزين مؤقت شخصي، بالطبع هذا ليس جديدًا، قبل بضعة أسابيع قرأت عن هذا في منشور في StackOverflow الذي كتبه Nick Craver حول كيفية إدارة ذاكرة التخزين المؤقت للتطبيق.

كما كنت أرغب دائمًا في العمل مع ذاكرة التخزين المؤقت، وكيف تعمل، والمنطق، وكيف يمكنني تشغيلها، ف… لماذا لا!

التدفق

فيما يلي تدفق سريع لكيفية تصرف الفئة التي تتحكم في ذاكرة التخزين المؤقت عندما يطلب المستخدم قيمة.

صورة من جيازو

التنفيذ

قبل أن نبدأ

أعرف، أعرف. يوجد System.Runtime.Caching الذي يتعامل مع ذاكرة التخزين المؤقت للذاكرة. لكنني قررت أن أقوم ببنائه بنفسي، إذا كنت تريد استخدام هذا الفصل، فراجع هنا لمعرفة الطريقة.

عنصر ذاكرة التخزين المؤقت

الخطوة الأولى هي أن يكون لديك فئة تخزن قيمة الكائن وتاريخ انتهاء الصلاحية. ربما هناك طريقة أفضل للقيام بذلك، ولكن هذا ما فكرت به، لذا إليك ما يلي:

public class CacheItem
{
    public string Identifier { get; set; }
    public object Value { get; set; }
    public DateTime ValidUntil { get; set; }

    public CacheItem(string identifier, object value, TimeSpan valid)
    {
        Identifier = identifier;
        Value = value;
        ValidUntil = DateTime.UtcNow.Add(valid);
    }
}

مستودع التخزين المؤقت

ثم نحتاج إلى فئة تتعامل مع الكائنات وتحفظها في مكان ما (مثل CacheItem). أحب التعامل مع جميع البيانات/النماذج في الفئات التي تحتوي على اللاحقة Repository، ولكن ليس عليك القيام بذلك، لذلك دعونا نبني واحدًا للتخزين المؤقت.

public class CacheRepository
{
    private static Dictionary<string, string> Cache = new Dictionary<string, string>();
    private static T Set<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
    {
        var item = new Models.Item(key, lookup(), durationMinutes);
        return Save<T>(key, item);
    }
    private static T Save<T>(string key, Item item)
    {
        Cache[key] = JsonConvert.SerializeObject(item);
        return (T)item.Value;
    }
    private static T Get<T>(string key)
    {
        var cached = Cache.FirstOrDefault(x => x.Key == key).Value;
        var item = string.IsNullOrEmpty(cached) ? null : JsonConvert.DeserializeObject<Item>(cached);
        return (item == null) 
        ? default : (item.ValidUntil > DateTime.UtcNow) 
        ? JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(item.Value)) : default;
    }
    public static T GetOrSet<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
    {
        var cache = Get<T>(key);
        return cache == null ? Set(key, lookup, durationMinutes) : cache;
    }
}

لدينا حتى الآن قاموس ثابت يسمى Cache حيث سيتم تخزين جميع العناصر. تذكر أن هذا لن يستمر إلا أثناء تشغيل التطبيق، ولهذا السبب يحتوي هذا العنوان البرنامج التعليمي على ذاكرة تخزين مؤقت في الذاكرة.

ضع في اعتبارك أنه سيتم تهيئة العنصر Cache بمجرد تحميل فئة CacheRepository.

الطريقة الوحيدة المتاحة عند استدعاء فئة CacheRepository هي GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes) التي تحتاج إلى ثلاث معلمات:

  1. key: معرف الكائن المراد حفظه.
  2. lookup: وظيفة رد الاتصال في حالة انتهاء صلاحية ذاكرة التخزين المؤقت أو أنها فارغة.
  3. durationMinutes: المدة بالدقائق والتي سيتم إضافتها إلى الوقت الحالي (بالتوقيت العالمي).

حان الوقت للتخزين المؤقت

الآن، استخدم مستودع التخزين المؤقت الخاص بنا للحصول على بعض البيانات من مكان ما.

لكي يكون كل هذا منطقيًا، فلنقم بإنشاء كائن مثال مع بعض الخصائص، ثم مستودعًا لجلب قائمة بهذا الكائن وملؤها.

public class User 
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
public class UserRepository 
{
    public List<User> Get()
    {
        // Code to get users
    }
}

نظرًا لأن لدينا طريقة لملء قائمة User، فلنستخدم فئة CacheRepository.

private UserRepository _userRepository = new UserRepository();
private List<User> _user;
public List<User> User
{
    get
    {
        _user = CacheRepository.GetOrSet($"users", usersRepo.Get, TimeSpan.FromMinutes(10));
        return _user;
    }
}

وهكذا، في كل مرة تقوم فيها بالوصول إلى المتغير User، سيطلب من CacheRepository قيمة الكائن الذي يحتوي على المفتاح users.

إذا كان هذا المفتاح موجودًا، فسوف يتحقق من تاريخ انتهاء الصلاحية. إذا كان أي من هذه الشروط خاطئًا، فسيتم استخدام رد الاتصال لتعيين قيمة الكائن (باستخدام usersRepo.Get) وحفظه في ذاكرة التخزين المؤقت مع تعيين تاريخ انتهاء الصلاحية على DateTime.UtcNow + TimeSpan.FromMinutes(10) وإعادته.