From de861ccd88afc32110ad196a9183817c51cda517 Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Sat, 15 May 2021 14:35:08 +0430 Subject: [PATCH] caching for pages --- .../Areas/Admin/Pages/ModifyPage.cshtml.cs | 13 +- .../Areas/User/Pages/MyComments.cshtml.cs | 16 ++ .../User/Pages/ReportedComments.cshtml.cs | 18 ++ GanjooRazor/Pages/Index.cshtml.cs | 3 + RMuseum/RMuseum.xml | 48 +++- RMuseum/Services/IGanjoorService.cs | 20 ++ .../Services/Implementation/GanjoorService.cs | 249 +++++++++++------- 7 files changed, 274 insertions(+), 93 deletions(-) diff --git a/GanjooRazor/Areas/Admin/Pages/ModifyPage.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/ModifyPage.cshtml.cs index 3a1280e8..9c0b5720 100644 --- a/GanjooRazor/Areas/Admin/Pages/ModifyPage.cshtml.cs +++ b/GanjooRazor/Areas/Admin/Pages/ModifyPage.cshtml.cs @@ -6,6 +6,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RMuseum.Models.Ganjoor; using RMuseum.Models.Ganjoor.ViewModels; +using RMuseum.Services; using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -14,6 +15,13 @@ namespace GanjooRazor.Areas.Admin.Pages { public class ModifyPageModel : PageModel { + /// + /// ganjoor service + /// + private readonly IGanjoorService _ganjoorService; + + + /// /// HttpClient instance /// @@ -23,9 +31,11 @@ namespace GanjooRazor.Areas.Admin.Pages /// constructor /// /// - public ModifyPageModel(HttpClient httpClient) + /// + public ModifyPageModel(HttpClient httpClient, IGanjoorService ganjoorService) { _httpClient = httpClient; + _ganjoorService = ganjoorService; } /// @@ -109,6 +119,7 @@ namespace GanjooRazor.Areas.Admin.Pages } else { + await _ganjoorService.CacheCleanForPageById(int.Parse(Request.Query["id"])); return Redirect($"/Admin/ModifyPage?id={Request.Query["id"]}&edit=true"); } } diff --git a/GanjooRazor/Areas/User/Pages/MyComments.cshtml.cs b/GanjooRazor/Areas/User/Pages/MyComments.cshtml.cs index fcc7de72..759fa37c 100644 --- a/GanjooRazor/Areas/User/Pages/MyComments.cshtml.cs +++ b/GanjooRazor/Areas/User/Pages/MyComments.cshtml.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RMuseum.Models.Ganjoor.ViewModels; +using RMuseum.Services; using RSecurityBackend.Models.Generic; namespace GanjooRazor.Areas.User.Pages @@ -19,6 +20,19 @@ namespace GanjooRazor.Areas.User.Pages [IgnoreAntiforgeryToken(Order = 1001)] public class MyCommentsModel : PageModel { + /// + /// ganjoor service + /// + private readonly IGanjoorService _ganjoorService; + + /// + /// constructor + /// + /// + public MyCommentsModel(IGanjoorService ganjoorService) + { + _ganjoorService = ganjoorService; + } // /// Last Error /// @@ -143,6 +157,7 @@ namespace GanjooRazor.Areas.User.Pages { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { + await _ganjoorService.CacheCleanForComment(id); var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/ganjoor/comment?id={id}"); if (response.StatusCode != HttpStatusCode.OK) @@ -161,6 +176,7 @@ namespace GanjooRazor.Areas.User.Pages { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { + await _ganjoorService.CacheCleanForComment(id); var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/comment/{id}", new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json")); if (response.StatusCode != HttpStatusCode.OK) { diff --git a/GanjooRazor/Areas/User/Pages/ReportedComments.cshtml.cs b/GanjooRazor/Areas/User/Pages/ReportedComments.cshtml.cs index f72d565f..143592f2 100644 --- a/GanjooRazor/Areas/User/Pages/ReportedComments.cshtml.cs +++ b/GanjooRazor/Areas/User/Pages/ReportedComments.cshtml.cs @@ -13,12 +13,27 @@ using Newtonsoft.Json.Linq; using RMuseum.Models.Ganjoor.ViewModels; using RSecurityBackend.Models.Generic; using System.Text; +using RMuseum.Services; namespace GanjooRazor.Areas.User.Pages { [IgnoreAntiforgeryToken(Order = 1001)] public class ReportedCommentsModel : PageModel { + /// + /// ganjoor service + /// + private readonly IGanjoorService _ganjoorService; + + /// + /// constructor + /// + /// + public ReportedCommentsModel(IGanjoorService ganjoorService) + { + _ganjoorService = ganjoorService; + } + /// /// Last Error /// @@ -156,6 +171,8 @@ namespace GanjooRazor.Areas.User.Pages reason = reasonText; break; } + await _ganjoorService.CacheCleanForComment(id); + var response = await secureClient.PostAsync($"{APIRoot.Url}/api/ganjoor/comment/moderate/{id}", new StringContent(JsonConvert.SerializeObject(reason), Encoding.UTF8, "application/json")); if (response.StatusCode != HttpStatusCode.OK) @@ -174,6 +191,7 @@ namespace GanjooRazor.Areas.User.Pages { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { + await _ganjoorService.CacheCleanForComment(id); var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/ganjoor/comment/report/{id}"); if (response.StatusCode != HttpStatusCode.OK) diff --git a/GanjooRazor/Pages/Index.cshtml.cs b/GanjooRazor/Pages/Index.cshtml.cs index 26d3b34e..c97b5c5a 100644 --- a/GanjooRazor/Pages/Index.cshtml.cs +++ b/GanjooRazor/Pages/Index.cshtml.cs @@ -222,6 +222,7 @@ namespace GanjooRazor.Pages } ), Encoding.UTF8, "application/json"); + await _ganjoorService.CacheCleanForPageById(poemId); var response = await secureClient.PostAsync($"{APIRoot.Url}/api/ganjoor/comment", stringContent); if (response.StatusCode == HttpStatusCode.OK) { @@ -289,6 +290,7 @@ namespace GanjooRazor.Pages { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { + await _ganjoorService.CacheCleanForComment(id); var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/ganjoor/comment?id={id}"); if (response.StatusCode != HttpStatusCode.OK) @@ -313,6 +315,7 @@ namespace GanjooRazor.Pages { if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) { + await _ganjoorService.CacheCleanForComment(id); var response = await secureClient.PutAsync($"{APIRoot.Url}/api/ganjoor/comment/{id}", new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json")); if (response.StatusCode != HttpStatusCode.OK) { diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index e8daf1e4..5b4c2ea5 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -6841,6 +6841,26 @@ + + + clean cache for paeg by id + + + + + + + clean cache for page by url + + + + + + clean cache for page by comment + + + + Get Verses By query @@ -7646,6 +7666,26 @@ + + + clean cache for paeg by id + + + + + + + clean cache for page by url + + + + + + clean cache for page by comment + + + + get page by url @@ -7961,7 +8001,12 @@ Image File Service - + + + IMemoryCache + + + constructor @@ -7971,6 +8016,7 @@ + diff --git a/RMuseum/Services/IGanjoorService.cs b/RMuseum/Services/IGanjoorService.cs index 5774bdc1..ae1a5aef 100644 --- a/RMuseum/Services/IGanjoorService.cs +++ b/RMuseum/Services/IGanjoorService.cs @@ -322,6 +322,26 @@ namespace RMuseum.Services /// RServiceResult HealthCheckContents(); + /// + /// clean cache for paeg by id + /// + /// + /// + Task CacheCleanForPageById(int id); + + /// + /// clean cache for page by url + /// + /// + void CacheCleanForPageByUrl(string url); + + /// + /// clean cache for page by comment + /// + /// + /// + Task CacheCleanForComment(int commentId); + /// /// Get Verses By query /// diff --git a/RMuseum/Services/Implementation/GanjoorService.cs b/RMuseum/Services/Implementation/GanjoorService.cs index 63c2d3b7..891db97b 100644 --- a/RMuseum/Services/Implementation/GanjoorService.cs +++ b/RMuseum/Services/Implementation/GanjoorService.cs @@ -19,6 +19,7 @@ using RSecurityBackend.Services.Implementation; using DNTPersianUtils.Core; using System.IO; using RSecurityBackend.Models.Image; +using Microsoft.Extensions.Caching.Memory; namespace RMuseum.Services.Implementation { @@ -325,6 +326,47 @@ namespace RMuseum.Services.Implementation } } + /// + /// clean cache for paeg by id + /// + /// + /// + public async Task CacheCleanForPageById(int id) + { + var dbPage = await _context.GanjoorPages.Where(p => p.Id == id).AsNoTracking().SingleOrDefaultAsync(); + if (dbPage != null) + { + CacheCleanForPageByUrl(dbPage.FullUrl); + } + } + + /// + /// clean cache for page by url + /// + /// + public void CacheCleanForPageByUrl(string url) + { + var cachKey = $"GanjoorService::GetPageByUrl::{url}"; + if (!_memoryCache.TryGetValue(cachKey, out GanjoorPageCompleteViewModel page)) + { + _memoryCache.Remove(cachKey); + } + } + + /// + /// clean cache for page by comment + /// + /// + /// + public async Task CacheCleanForComment(int commentId) + { + var comment = await _context.GanjoorComments.Where(c => c.Id == commentId).SingleOrDefaultAsync(); + if(comment != null) + { + await CacheCleanForPageById(comment.PoemId); + } + } + /// /// get page by url /// @@ -346,109 +388,117 @@ namespace RMuseum.Services.Implementation url = url.Substring(0, url.Length - 1); } - var dbPage = await _context.GanjoorPages.Where(p => p.FullUrl == url).AsNoTracking().SingleOrDefaultAsync(); - if (dbPage == null) - return new RServiceResult(null); //not found - var secondPoet = dbPage.SecondPoetId == null ? null : - await - (from poet in _context.GanjoorPoets - join cat in _context.GanjoorCategories.Where(c => c.ParentId == null) - on poet.Id equals cat.PoetId - where poet.Id == (int)dbPage.SecondPoetId - orderby poet.Name descending - select new GanjoorPoetViewModel() - { - Id = poet.Id, - Name = poet.Name, - FullUrl = cat.FullUrl, - RootCatId = cat.Id, - Nickname = poet.Nickname, - Published = poet.Published, - ImageUrl = poet.RImageId == null ? "" : $"/api/ganjoor/poet/image{cat.FullUrl}.png" - } - ) - .AsNoTracking().SingleAsync(); - GanjoorPageCompleteViewModel page = new GanjoorPageCompleteViewModel() + url = url.Replace("//", "/"); //duplicated slashes would be merged + + var cachKey = $"GanjoorService::GetPageByUrl::{url}"; + if (!_memoryCache.TryGetValue(cachKey, out GanjoorPageCompleteViewModel page)) { - Id = dbPage.Id, - GanjoorPageType = dbPage.GanjoorPageType, - Title = dbPage.Title, - FullTitle = dbPage.FullTitle, - UrlSlug = dbPage.UrlSlug, - FullUrl = dbPage.FullUrl, - HtmlText = dbPage.HtmlText, - SecondPoet = secondPoet + var dbPage = await _context.GanjoorPages.Where(p => p.FullUrl == url).AsNoTracking().SingleOrDefaultAsync(); + if (dbPage == null) + return new RServiceResult(null); //not found + var secondPoet = dbPage.SecondPoetId == null ? null : + await + (from poet in _context.GanjoorPoets + join cat in _context.GanjoorCategories.Where(c => c.ParentId == null) + on poet.Id equals cat.PoetId + where poet.Id == (int)dbPage.SecondPoetId + orderby poet.Name descending + select new GanjoorPoetViewModel() + { + Id = poet.Id, + Name = poet.Name, + FullUrl = cat.FullUrl, + RootCatId = cat.Id, + Nickname = poet.Nickname, + Published = poet.Published, + ImageUrl = poet.RImageId == null ? "" : $"/api/ganjoor/poet/image{cat.FullUrl}.png" + } + ) + .AsNoTracking().SingleAsync(); + page = new GanjoorPageCompleteViewModel() + { + Id = dbPage.Id, + GanjoorPageType = dbPage.GanjoorPageType, + Title = dbPage.Title, + FullTitle = dbPage.FullTitle, + UrlSlug = dbPage.UrlSlug, + FullUrl = dbPage.FullUrl, + HtmlText = dbPage.HtmlText, + SecondPoet = secondPoet - }; - switch (page.GanjoorPageType) - { - case GanjoorPageType.PoemPage: - { - var poemRes = await GetPoemById((int)dbPage.PoemId); - if (!string.IsNullOrEmpty(poemRes.ExceptionString)) + }; + switch (page.GanjoorPageType) + { + case GanjoorPageType.PoemPage: { - return new RServiceResult(null, poemRes.ExceptionString); - } - page.Poem = poemRes.Result; - } - break; - - case GanjoorPageType.CatPage: - { - var catRes = await GetCatById((int)dbPage.CatId); - if (!string.IsNullOrEmpty(catRes.ExceptionString)) - { - return new RServiceResult(null, catRes.ExceptionString); - } - page.PoetOrCat = catRes.Result; - } - break; - default: - { - if (dbPage.PoetId != null) - { - var poetRes = await GetPoetById((int)dbPage.PoetId); - if (!string.IsNullOrEmpty(poetRes.ExceptionString)) + var poemRes = await GetPoemById((int)dbPage.PoemId); + if (!string.IsNullOrEmpty(poemRes.ExceptionString)) { - return new RServiceResult(null, poetRes.ExceptionString); + return new RServiceResult(null, poemRes.ExceptionString); } - page.PoetOrCat = poetRes.Result; + page.Poem = poemRes.Result; + } + break; - var pre = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && - ((p.PageOrder < dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id < dbPage.Id))) - .OrderByDescending(p => p.PageOrder) - .ThenByDescending(p => p.Id) - .AsNoTracking() - .FirstOrDefaultAsync(); - if(pre != null) + case GanjoorPageType.CatPage: + { + var catRes = await GetCatById((int)dbPage.CatId); + if (!string.IsNullOrEmpty(catRes.ExceptionString)) { - page.Previous = new GanjoorPageSummaryViewModel() + return new RServiceResult(null, catRes.ExceptionString); + } + page.PoetOrCat = catRes.Result; + } + break; + default: + { + if (dbPage.PoetId != null) + { + var poetRes = await GetPoetById((int)dbPage.PoetId); + if (!string.IsNullOrEmpty(poetRes.ExceptionString)) { - Id = pre.Id, - Title = pre.Title, - FullUrl = pre.FullUrl - }; - } + return new RServiceResult(null, poetRes.ExceptionString); + } + page.PoetOrCat = poetRes.Result; - var next = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && - ((p.PageOrder > dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id > dbPage.Id))) - .OrderBy(p => p.PageOrder) - .ThenBy(p => p.Id) - .AsNoTracking() - .FirstOrDefaultAsync(); - if (next != null) - { - page.Next = new GanjoorPageSummaryViewModel() + var pre = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && + ((p.PageOrder < dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id < dbPage.Id))) + .OrderByDescending(p => p.PageOrder) + .ThenByDescending(p => p.Id) + .AsNoTracking() + .FirstOrDefaultAsync(); + if (pre != null) { - Id = next.Id, - Title = next.Title, - FullUrl = next.FullUrl - }; + page.Previous = new GanjoorPageSummaryViewModel() + { + Id = pre.Id, + Title = pre.Title, + FullUrl = pre.FullUrl + }; + } + + var next = await _context.GanjoorPages.Where(p => p.GanjoorPageType == page.GanjoorPageType && p.ParentId == dbPage.ParentId && p.PoetId == dbPage.PoetId && + ((p.PageOrder > dbPage.PageOrder) || (p.PageOrder == dbPage.PageOrder && p.Id > dbPage.Id))) + .OrderBy(p => p.PageOrder) + .ThenBy(p => p.Id) + .AsNoTracking() + .FirstOrDefaultAsync(); + if (next != null) + { + page.Next = new GanjoorPageSummaryViewModel() + { + Id = next.Id, + Title = next.Title, + FullUrl = next.FullUrl + }; + } } } - } - break; + break; + } + _memoryCache.Set(cachKey, page); } + return new RServiceResult(page); } @@ -627,6 +677,7 @@ namespace RMuseum.Services.Implementation } } + await CacheCleanForPageById(poemId); return new RServiceResult @@ -668,6 +719,8 @@ namespace RMuseum.Services.Implementation return new RServiceResult(false); //not found } + await CacheCleanForComment(commentId); + htmlComment = htmlComment.ApplyCorrectYeKe(); comment.HtmlComment = htmlComment; @@ -699,6 +752,8 @@ namespace RMuseum.Services.Implementation return new RServiceResult(false); //not found } + await CacheCleanForComment(commentId); + if (comment.UserId != null) { reason = string.IsNullOrEmpty(reason) ? "" : $"علت ارائه شده برای حذف یا متن گزارش کاربر شاکی: {Environment.NewLine}" + @@ -756,6 +811,8 @@ namespace RMuseum.Services.Implementation return new RServiceResult(false); //not found } + await CacheCleanForComment(commentId); + //if user has got replies, delete them and notify their owners of what happened var replies = await _FindReplies(comment); for (int i = replies.Count - 1; i >= 0; i--) @@ -2021,6 +2078,9 @@ namespace RMuseum.Services.Implementation await _context.SaveChangesAsync(); + CacheCleanForPageByUrl(dbPage.FullUrl); + + return await GetPageByUrl(dbPage.FullUrl); } catch(Exception exp) @@ -2348,6 +2408,11 @@ namespace RMuseum.Services.Implementation /// protected readonly IImageFileService _imageFileService; + /// + /// IMemoryCache + /// + protected readonly IMemoryCache _memoryCache; + /// @@ -2359,13 +2424,15 @@ namespace RMuseum.Services.Implementation /// /// /// - public GanjoorService(RMuseumDbContext context, IConfiguration configuration, IBackgroundTaskQueue backgroundTaskQueue, IAppUserService appUserService, IRNotificationService notificationService, IImageFileService imageFileService) + /// + public GanjoorService(RMuseumDbContext context, IConfiguration configuration, IBackgroundTaskQueue backgroundTaskQueue, IAppUserService appUserService, IRNotificationService notificationService, IImageFileService imageFileService, IMemoryCache memoryCache) { _context = context; _backgroundTaskQueue = backgroundTaskQueue; _appUserService = appUserService; _notificationService = notificationService; _imageFileService = imageFileService; + _memoryCache = memoryCache; Configuration = configuration; } }