اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
دو دقیقه
در سری مقالات پیاده سازی CQRS توسط کتابخانه MediatR، مطالبی جهت آشنایی و نحوه استفاده از این کتابخانه بیان شدهاست که در بخش چهارم، به رفتارها (Behavior)ها جهت اعمالی از جمله اعتبارسنجی، ثبت وقایع و ... پرداخته شدهاست. در این مقاله قصد داریم با استفاده از رفتارها، اقدام به پیاده سازی کش، برای خروجی حاصل از کوئریها نماییم.
با توجه به اینکه برای کش کردن دادهها، نیاز به یک کلید منحصر بفرد داریم، اینترفیس فوق، داری یک خصوصیت رشتهای Key میباشد و تمامی درخواستهایی که نیاز به کش کردن دارند، باید Key را با یک مقدار منحصر بفرد مقدار دهی کنند. در کدهای پایین، یک نمونه از یک درخواست را مشاهده میکنید که مقدار Key را برابر با نام کلاس، به همراه شناسه رکورد مورد نظر مقدار دهی کردهاست:
همانطور که قابل مشاهدهاست، ابتدا کش با مقدار کلید درخواست جاری بررسی میشود و اگر مقداری در کش موجود باشد همان را بر میگرداند، در غیر این صورت هندلر مربوطه اجرا خواهد شد و سپس مقدار دریافت شده از خروجی هندلر را کش خواهد کرد.
با استفاده از رفتارها، تمامی کدهای لازم برای خواندن و ثبت دادهها از کش، در Behavior مربوطه پیاده سازی خواهد شد و نیازی نیست در تمامی هندلرهای مربوط به درخواستهای از نوع Query، کدهای مربوط به Caching تکرار شود.
ابتدا یک interface همانند زیر ایجاد خواهیم کرد و هدف از آن، محدود کردن Caching برای درخواستهایی خواهد بود که از این اینترفیس ارث بری کردهاند و نه تمامی درخواستها:
public interface ICachableQuery { public string Key { get;} }
public class GetBookmarkQuery : IRequest<BookmarkDto>, ICachableQuer { public int Id { get; init; } public string Key => $"{nameof(GetBookmarkQuery)}-{Id}"; public GetBookmarkQuery(int id) { Id = id; } }
و در ادامه، یک Behavior به اسم Caching Behavior ایجاد و کدهای لازم برای خواندن و ثبت دادهها از کش را پیاده سازی کردهایم:
public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : ICachableQuer { private readonly IDistributedCache _cache; public CachingBehavior(IDistributedCache distributedCache) { _cache = distributedCache; } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { TResponse response; var cachedResponse = await _cache.GetAsync(request.Key, cancellationToken); if (cachedResponse != null) response = JsonConvert.DeserializeObject<TResponse>(Encoding.Default.GetString(cachedResponse)); else { response = await next(); var serialized = Encoding.Default.GetBytes(JsonConvert.SerializeObject(response)); await _cache.SetAsync(request.Key, serialized, cancellationToken); } return response; } }
و در پایان نیاز هست این Behavior را به صورت زیر ثبت نماییم :
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(CachingBehavior <,>));
با توجه با اینکه ممکن است بعد از کش کردن خروجی یک کوئری، تغییراتی بر روی رکورد مورد نظر که کش شده است انجام شود، بهتر است تاریخ انقضا برای کشها ثبت شود و یا بعد از به روزرسانی یک رکورد، کش مربوطه را به روزرسانی کنید.