소스 검색

PooledList

懒得勤快 3 년 전
부모
커밋
19b6e8790e
44개의 변경된 파일211개의 추가작업 그리고 128개의 파일을 삭제
  1. 2 1
      src/Masuit.MyBlogs.Core/Common/CommonHelper.cs
  2. 2 1
      src/Masuit.MyBlogs.Core/Common/ImagebedClient.cs
  3. 2 1
      src/Masuit.MyBlogs.Core/Controllers/AdminController.cs
  4. 3 1
      src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs
  5. 2 1
      src/Masuit.MyBlogs.Core/Controllers/BaseController.cs
  6. 4 2
      src/Masuit.MyBlogs.Core/Controllers/CategoryController.cs
  7. 7 7
      src/Masuit.MyBlogs.Core/Controllers/CommentController.cs
  8. 1 1
      src/Masuit.MyBlogs.Core/Controllers/Drive/AdminController.cs
  9. 4 3
      src/Masuit.MyBlogs.Core/Controllers/FileController.cs
  10. 5 4
      src/Masuit.MyBlogs.Core/Controllers/HomeController.cs
  11. 4 2
      src/Masuit.MyBlogs.Core/Controllers/MenuController.cs
  12. 9 9
      src/Masuit.MyBlogs.Core/Controllers/MsgController.cs
  13. 9 2
      src/Masuit.MyBlogs.Core/Controllers/PostController.cs
  14. 5 4
      src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs
  15. 1 2
      src/Masuit.MyBlogs.Core/Controllers/SystemController.cs
  16. 11 0
      src/Masuit.MyBlogs.Core/Infrastructure/DataContext.cs
  17. 11 3
      src/Masuit.MyBlogs.Core/Infrastructure/Drive/DriveAccountService.cs
  18. 14 10
      src/Masuit.MyBlogs.Core/Infrastructure/Drive/DriveService.cs
  19. 4 2
      src/Masuit.MyBlogs.Core/Infrastructure/Drive/IDriveAccountService.cs
  20. 4 1
      src/Masuit.MyBlogs.Core/Infrastructure/Drive/IDriveService.cs
  21. 15 14
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/BaseRepository.cs
  22. 9 8
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs
  23. 1 1
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/ISearchDetailsRepository.cs
  24. 4 3
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/PostRepository.cs
  25. 1 1
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/SearchDetailsRepository.cs
  26. 12 3
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IBaseService.cs
  27. 1 1
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IPostService.cs
  28. 1 2
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/ISearchDetailsService.cs
  29. 4 3
      src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs
  30. 1 0
      src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj
  31. 18 5
      src/Masuit.MyBlogs.Core/Models/ViewModel/HomePageViewModel.cs
  32. 2 1
      src/Masuit.MyBlogs.Core/PrepareStartup.cs
  33. 2 1
      src/Masuit.MyBlogs.Core/Views/Dashboard/Counter.razor
  34. 9 8
      src/Masuit.MyBlogs.Core/Views/Home/Category.cshtml
  35. 1 1
      src/Masuit.MyBlogs.Core/Views/Home/Index.cshtml
  36. 1 1
      src/Masuit.MyBlogs.Core/Views/Post/CompareVersion.cshtml
  37. 5 5
      src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml
  38. 5 5
      src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml
  39. 2 2
      src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml
  40. 1 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml
  41. 1 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml
  42. 3 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial.cshtml
  43. 3 1
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial_Admin.cshtml
  44. 5 2
      src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml

+ 2 - 1
src/Masuit.MyBlogs.Core/Common/CommonHelper.cs

@@ -18,6 +18,7 @@ using System.Drawing;
 using System.Net;
 using System.Net.Sockets;
 using System.Text;
+using Collections.Pooled;
 using TimeZoneConverter;
 using ArgumentException = System.ArgumentException;
 
@@ -308,7 +309,7 @@ namespace Masuit.MyBlogs.Core.Common
             }
 
             var elements = doc.DocumentElement.QuerySelectorAll("p,br");
-            var els = elements.OrderByRandom().Take(Math.Max(elements.Length / 5, 3)).ToList();
+            using var els = elements.OrderByRandom().Take(Math.Max(elements.Length / 5, 3)).ToPooledList();
             var href = "https://" + SystemSettings["Domain"].Split('|').OrderByRandom().FirstOrDefault();
             foreach (var el in els)
             {

+ 2 - 1
src/Masuit.MyBlogs.Core/Common/ImagebedClient.cs

@@ -7,6 +7,7 @@ using Masuit.Tools.Systems;
 using System.Net.Http.Headers;
 using System.Text.RegularExpressions;
 using System.Web;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Common
 {
@@ -45,7 +46,7 @@ namespace Masuit.MyBlogs.Core.Common
             }
 
             file = Regex.Replace(Path.GetFileName(file), @"\p{P}|\p{S}", "");
-            var gitlabs = AppConfig.GitlabConfigs.Where(c => c.FileLimitSize >= stream.Length && !_failedList.Contains(c.ApiUrl)).OrderByRandom().ToList();
+            using var gitlabs = AppConfig.GitlabConfigs.Where(c => c.FileLimitSize >= stream.Length && !_failedList.Contains(c.ApiUrl)).OrderByRandom().ToPooledList();
             if (gitlabs.Count > 0)
             {
                 var gitlab = gitlabs[0];

+ 2 - 1
src/Masuit.MyBlogs.Core/Controllers/AdminController.cs

@@ -1,4 +1,5 @@
 using AutoMapper;
+using Collections.Pooled;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Configs;
 using Masuit.MyBlogs.Core.Extensions;
@@ -75,7 +76,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 }
             }
             if (ModelState.IsValid) return;
-            var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).ToList();
+            using var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).ToPooledList();
             if (errmsgs.Any())
             {
                 for (var i = 0; i < errmsgs.Count; i++)

+ 3 - 1
src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs

@@ -1,4 +1,5 @@
 using AutoMapper.QueryableExtensions;
+using Collections.Pooled;
 using EFCoreSecondLevelCacheInterceptor;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Extensions;
@@ -187,7 +188,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpGet("/partner/{id}/records-export"), MyAuthorize]
         public IActionResult ExportClickRecords(int id)
         {
-            using var ms = ClickRecordService.GetQuery<DateTime, AdvertisementClickRecordViewModel>(e => e.AdvertisementId == id, e => e.Time, false).ToList().ToDataTable().ToExcel();
+            using var list = ClickRecordService.GetQuery<DateTime, AdvertisementClickRecordViewModel>(e => e.AdvertisementId == id, e => e.Time, false).ToPooledList();
+            using var ms = list.ToDataTable().ToExcel();
             var advertisement = AdsService[id];
             return this.ResumeFile(ms.ToArray(), ContentType.Xlsx, advertisement.Title + "访问记录.xlsx");
         }

+ 2 - 1
src/Masuit.MyBlogs.Core/Controllers/BaseController.cs

@@ -1,4 +1,5 @@
 using AutoMapper;
+using Collections.Pooled;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Common.Mails;
 using Masuit.MyBlogs.Core.Configs;
@@ -153,7 +154,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
 
             if (ModelState.IsValid) return next();
-            var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).Select((s, i) => $"{i + 1}. {s}").ToList();
+            using var errmsgs = ModelState.SelectMany(kv => kv.Value.Errors.Select(e => e.ErrorMessage)).Select((s, i) => $"{i + 1}. {s}").ToPooledList();
             filterContext.Result = true switch
             {
                 _ when Request.HasJsonContentType() || Request.Method == HttpMethods.Post => ResultData(errmsgs, false, "数据校验失败,错误信息:" + errmsgs.Join(" | "), user != null, HttpStatusCode.BadRequest),

+ 4 - 2
src/Masuit.MyBlogs.Core/Controllers/CategoryController.cs

@@ -1,4 +1,5 @@
-using Masuit.MyBlogs.Core.Common;
+using Collections.Pooled;
+using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.Command;
@@ -28,7 +29,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetCategories()
         {
-            var list = CategoryService.GetQueryNoTracking(c => c.Status == Status.Available, c => c.Name).ToList().ToTree(c => c.Id, c => c.ParentId);
+            using var categories = CategoryService.GetQueryNoTracking(c => c.Status == Status.Available, c => c.Name).ToPooledList();
+            var list = categories.ToTree(c => c.Id, c => c.ParentId);
             return ResultData(list.Mapper<List<CategoryDto>>());
         }
 

+ 7 - 7
src/Masuit.MyBlogs.Core/Controllers/CommentController.cs

@@ -23,6 +23,7 @@ using System.Text.RegularExpressions;
 using Masuit.Tools.Systems;
 using Microsoft.EntityFrameworkCore;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Controllers
 {
@@ -152,7 +153,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                         Link = Url.Action("Details", "Post", new { id = comment.PostId, cid = comment.Id }) + "#comment"
                     });
                 }
-                if (comment.ParentId == 0)
+                if (comment.ParentId == null)
                 {
                     emails.Add(post.Email);
                     emails.Add(post.ModifierEmail);
@@ -223,7 +224,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             if (cid > 0)
             {
                 var comment = await CommentService.GetByIdAsync(cid.Value) ?? throw new NotFoundException("评论未找到");
-                var layer = CommentService.GetQueryNoTracking(c => c.GroupTag == comment.GroupTag).ToList();
+                using var layer = CommentService.GetQueryNoTracking(c => c.GroupTag == comment.GroupTag).ToPooledList();
                 foreach (var c in layer)
                 {
                     c.CommentDate = c.CommentDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
@@ -253,7 +254,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
             int total = parent.TotalCount; //总条数,用于前台分页
             var tags = parent.Data.Select(c => c.GroupTag).ToArray();
-            var comments = CommentService.GetQuery(c => tags.Contains(c.GroupTag)).Include(c => c.Post).AsNoTracking().ToList();
+            using var comments = CommentService.GetQuery(c => tags.Contains(c.GroupTag)).Include(c => c.Post).AsNoTracking().ToPooledList();
             comments.ForEach(c =>
             {
                 c.CommentDate = c.CommentDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
@@ -288,7 +289,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [MyAuthorize]
         public async Task<ActionResult> Pass(int id)
         {
-            Comment comment = await CommentService.GetByIdAsync(id) ?? throw new NotFoundException("评论不存在!");
+            var comment = await CommentService.GetByIdAsync(id) ?? throw new NotFoundException("评论不存在!");
             comment.Status = Status.Published;
             Post post = await PostService.GetByIdAsync(comment.PostId);
             bool b = await CommentService.SaveChangesAsync() > 0;
@@ -299,12 +300,11 @@ namespace Masuit.MyBlogs.Core.Controllers
                     .Set("time", DateTime.Now.ToTimeZoneF(HttpContext.Session.Get<string>(SessionKey.TimeZone)))
                     .Set("nickname", comment.NickName)
                     .Set("content", comment.Content);
-                var root = comment.Root();
-                var emails = root.Flatten().Select(c => c.Email).Append(post.ModifierEmail).Except(new List<string> { comment.Email, CurrentUser.Email }).ToHashSet();
+                using var emails = CommentService.GetQuery(c => c.GroupTag == comment.GroupTag).Select(c => c.Email).Distinct().ToPooledList().Append(post.ModifierEmail).Except(new List<string> { comment.Email, CurrentUser.Email }).ToPooledSet();
                 var link = Url.Action("Details", "Post", new
                 {
                     id = comment.PostId,
-                    cid = root.Id
+                    cid = id
                 }, Request.Scheme) + "#comment";
                 foreach (var email in emails)
                 {

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/Drive/AdminController.cs

@@ -98,7 +98,7 @@ namespace Masuit.MyBlogs.Core.Controllers.Drive
         {
             try
             {
-                List<DriveAccountService.DriveInfo> driveInfo = new List<DriveAccountService.DriveInfo>();
+                var driveInfo = new List<DriveAccountService.DriveInfo>();
                 if (_setting.Get("AccountStatus") == "已认证")
                 {
                     driveInfo = await _driveAccount.GetDriveInfo();

+ 4 - 3
src/Masuit.MyBlogs.Core/Controllers/FileController.cs

@@ -1,4 +1,5 @@
-using Masuit.MyBlogs.Core.Common;
+using Collections.Pooled;
+using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Extensions;
 using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
@@ -34,11 +35,11 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetFiles(string path)
         {
-            var files = Directory.GetFiles(HostEnvironment.WebRootPath + path).OrderByDescending(s => s).Select(s => new
+            using var files = Directory.GetFiles(HostEnvironment.WebRootPath + path).OrderByDescending(s => s).Select(s => new
             {
                 filename = Path.GetFileName(s),
                 path = s
-            }).ToList();
+            }).ToPooledList();
             return ResultData(files);
         }
 

+ 5 - 4
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -1,5 +1,6 @@
 using AngleSharp;
 using AutoMapper.QueryableExtensions;
+using Collections.Pooled;
 using EFCoreSecondLevelCacheInterceptor;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Extensions;
@@ -327,18 +328,18 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var postsQuery = PostService.GetQuery<PostDto>(PostBaseWhere().And(p => p.Status == Status.Published)); //准备文章的查询
             var notices = await NoticeService.GetPagesFromCacheAsync<DateTime, NoticeDto>(1, 5, n => n.NoticeStatus == NoticeStatus.Normal, n => n.ModifyDate, false); //加载前5条公告
-            var cats = await CategoryService.GetQuery(c => c.Status == Status.Available && c.Post.Count > 0, c => c.Name).Include(c => c.Parent).Cacheable().ToListAsync(); //加载分类目录
-            var hotSearches = RedisHelper.Get<List<KeywordsRank>>("SearchRank:Week").Take(10).ToList(); //热词统计
+            using var cats = CategoryService.GetQuery(c => c.Status == Status.Available && c.Post.Count > 0).Include(c => c.Parent).OrderBy(c => c.Name).ThenBy(c => c.Path).AsNoTracking().Cacheable().ToPooledList(); //加载分类目录
+            var hotSearches = RedisHelper.Get<PooledList<KeywordsRank>>("SearchRank:Week").Take(10).ToPooledList(); //热词统计
             var hot5Post = postsQuery.OrderBy((new Random().Next() % 3) switch
             {
                 1 => nameof(OrderBy.VoteUpCount),
                 2 => nameof(OrderBy.AverageViewCount),
                 _ => nameof(OrderBy.TotalViewCount)
-            } + " desc").Skip(0).Take(5).Cacheable().ToList(); //热门文章
+            } + " desc").Skip(0).Take(5).Cacheable().ToPooledList(); //热门文章
             var tagdic = PostService.GetTags().OrderByRandom().Take(20).ToDictionary(x => x.Key, x => Math.Min(x.Value + 12, 32)); //统计标签
             return new HomePageViewModel
             {
-                Categories = Mapper.Map<List<CategoryDto_P>>(cats.OrderBy(c => c.Path()).ToList()),
+                Categories = Mapper.Map<PooledList<CategoryDto_P>>(cats.ToTree(c => c.Id, c => c.ParentId).Flatten().ToPooledList()),
                 HotSearch = hotSearches,
                 Notices = notices.Data,
                 Tags = tagdic,

+ 4 - 2
src/Masuit.MyBlogs.Core/Controllers/MenuController.cs

@@ -1,4 +1,5 @@
-using DocumentFormat.OpenXml.Office.Word;
+using Collections.Pooled;
+using DocumentFormat.OpenXml.Office.Word;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
 using Masuit.MyBlogs.Core.Models.Command;
@@ -29,7 +30,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         public ActionResult GetMenus()
         {
-            var menus = MenuService.GetAllNoTracking(m => m.Sort).ToList().ToTree(m => m.Id, m => m.ParentId);
+            using var list = MenuService.GetAllNoTracking(m => m.Sort).ToPooledList();
+            var menus = list.ToTree(m => m.Id, m => m.ParentId);
             return ResultData(Mapper.Map<List<MenuDto>>(menus));
         }
 

+ 9 - 9
src/Masuit.MyBlogs.Core/Controllers/MsgController.cs

@@ -23,6 +23,7 @@ using System.Text;
 using System.Text.RegularExpressions;
 using Masuit.Tools.Systems;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Controllers
 {
@@ -52,7 +53,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("msg"), Route("msg/{cid:int}"), ResponseCache(Duration = 600, VaryByHeader = "Cookie")]
         public async Task<ActionResult> Index()
         {
-            ViewBag.TotalCount = LeaveMessageService.Count(m => m.ParentId == 0 && m.Status == Status.Published);
+            ViewBag.TotalCount = LeaveMessageService.Count(m => m.ParentId == null && m.Status == Status.Published);
             var text = await new FileInfo(Path.Combine(HostEnvironment.WebRootPath, "template", "agreement.html")).ShareReadWrite().ReadAllTextAsync(Encoding.UTF8);
             return CurrentUser.IsAdmin ? View("Index_Admin", text) : View(model: text);
         }
@@ -69,7 +70,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             if (cid > 0)
             {
                 var message = await LeaveMessageService.GetByIdAsync(cid.Value) ?? throw new NotFoundException("留言未找到");
-                var layer = LeaveMessageService.GetQueryNoTracking(e => e.GroupTag == message.GroupTag).ToList();
+                using var layer = LeaveMessageService.GetQueryNoTracking(e => e.GroupTag == message.GroupTag).ToPooledList();
                 foreach (var m in layer)
                 {
                     m.PostDate = m.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
@@ -98,7 +99,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             }
             var total = parent.TotalCount;
             var tags = parent.Data.Select(c => c.GroupTag).ToArray();
-            var messages = LeaveMessageService.GetQueryNoTracking(c => tags.Contains(c.GroupTag)).ToList();
+            using var messages = LeaveMessageService.GetQueryNoTracking(c => tags.Contains(c.GroupTag)).ToPooledList();
             messages.ForEach(m =>
             {
                 m.PostDate = m.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
@@ -219,7 +220,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                         Link = Url.Action("Index", "Msg", new { cid = msg.Id })
                     });
                 }
-                if (msg.ParentId == 0)
+                if (msg.ParentId == null)
                 {
                     //新评论,只通知博主
                     BackgroundJob.Enqueue(() => CommonHelper.SendMail(Request.Host + "|博客新留言:", content.Set("link", Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme)).Render(false), email, ClientIP));
@@ -227,7 +228,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 else
                 {
                     //通知博主和上层所有关联的评论访客
-                    var emails = LeaveMessageService.GetQuery(e => e.GroupTag == msg.GroupTag).Select(c => c.Email).Distinct().AsEnumerable().Append(email).Except(new[] { msg.Email }).ToHashSet();
+                    using var emails = LeaveMessageService.GetQuery(e => e.GroupTag == msg.GroupTag).Select(c => c.Email).Distinct().AsEnumerable().Append(email).Except(new[] { msg.Email }).ToPooledSet();
                     string link = Url.Action("Index", "Msg", new { cid = msg.Id }, Request.Scheme);
                     foreach (var s in emails)
                     {
@@ -257,10 +258,9 @@ namespace Masuit.MyBlogs.Core.Controllers
             bool b = await LeaveMessageService.SaveChangesAsync() > 0;
             if (b)
             {
-                var root = msg.Root();
                 var content = new Template(await new FileInfo(Path.Combine(HostEnvironment.WebRootPath, "template", "notify.html")).ShareReadWrite().ReadAllTextAsync(Encoding.UTF8)).Set("time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Set("nickname", msg.NickName).Set("content", msg.Content);
-                var emails = root.Flatten().Select(c => c.Email).Except(new List<string> { msg.Email, CurrentUser.Email }).ToHashSet();
-                var link = Url.Action("Index", "Msg", new { cid = root.Id }, Request.Scheme);
+                using var emails = LeaveMessageService.GetQuery(m => m.GroupTag == msg.GroupTag).Select(m => m.Email).Distinct().ToPooledList().Except(new List<string> { msg.Email, CurrentUser.Email }).ToPooledSet();
+                var link = Url.Action("Index", "Msg", new { cid = id }, Request.Scheme);
                 foreach (var s in emails)
                 {
                     BackgroundJob.Enqueue(() => CommonHelper.SendMail($"{Request.Host}{CommonHelper.SystemSettings["Title"]} 留言回复:", content.Set("link", link).Render(false), s, ClientIP));
@@ -377,7 +377,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [MyAuthorize]
         public ActionResult GetUnreadMsgs()
         {
-            var msgs = MessageService.GetQueryNoTracking(m => !m.Read, m => m.Time, false).NotCacheable().ToList();
+            using var msgs = MessageService.GetQueryNoTracking(m => !m.Read, m => m.Time, false).NotCacheable().ToPooledList();
             return ResultData(msgs);
         }
 

+ 9 - 2
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -1,5 +1,6 @@
 using AngleSharp;
 using CacheManager.Core;
+using Collections.Pooled;
 using EFCoreSecondLevelCacheInterceptor;
 using Hangfire;
 using JiebaNet.Segmenter;
@@ -68,6 +69,8 @@ namespace Masuit.MyBlogs.Core.Controllers
 
         public IPostVisitRecordService PostVisitRecordService { get; set; }
 
+        public ICommentService CommentService { get; set; }
+
         /// <summary>
         /// 文章详情页
         /// </summary>
@@ -81,7 +84,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return RedirectToAction("Details", cid > 0 ? new { id, kw, cid, t = SnowFlake.NewId } : new { id, kw, t = SnowFlake.NewId });
             }
 
-            var post = await PostService.GetAsync(p => p.Id == id && (p.Status == Status.Published || CurrentUser.IsAdmin)) ?? throw new NotFoundException("文章未找到");
+            var post = await PostService.GetQuery(p => p.Id == id && (p.Status == Status.Published || CurrentUser.IsAdmin)).Include(p => p.Seminar).FirstOrDefaultAsync() ?? throw new NotFoundException("文章未找到");
             CheckPermission(post);
             if (!string.IsNullOrEmpty(post.Redirect))
             {
@@ -94,6 +97,9 @@ namespace Masuit.MyBlogs.Core.Controllers
                 return Redirect(post.Redirect);
             }
 
+            post.Category = CategoryService[post.CategoryId];
+            ViewBag.CommentsCount = CommentService.Count(c => c.PostId == id && c.ParentId == null && c.Status == Status.Published);
+            ViewBag.HistoryCount = PostHistoryVersionService.Count(c => c.PostId == id);
             ViewBag.Keyword = post.Keyword + "," + post.Label;
             ViewBag.Desc = await post.Content.GetSummary(200);
             var modifyDate = post.ModifyDate;
@@ -1084,7 +1090,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         [ProducesResponseType(typeof(PagedList<PostVisitRecordViewModel>), (int)HttpStatusCode.OK)]
         public IActionResult ExportPostVisitRecords(int id)
         {
-            using var ms = PostVisitRecordService.GetQuery<DateTime, PostVisitRecordViewModel>(e => e.PostId == id, e => e.Time, false).ToList().ToDataTable().ToExcel();
+            using var list = PostVisitRecordService.GetQuery<DateTime, PostVisitRecordViewModel>(e => e.PostId == id, e => e.Time, false).ToPooledList();
+            using var ms = list.ToDataTable().ToExcel();
             var post = PostService[id];
             return this.ResumeFile(ms.ToArray(), ContentType.Xlsx, post.Title + "访问记录.xlsx");
         }

+ 5 - 4
src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs

@@ -18,6 +18,7 @@ using System.Text.RegularExpressions;
 using EFCoreSecondLevelCacheInterceptor;
 using Microsoft.EntityFrameworkCore;
 using WilderMinds.RssSyndication;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Controllers
 {
@@ -45,7 +46,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var time = DateTime.Today.AddDays(-1);
             string scheme = Request.Scheme;
             var host = Request.Host;
-            var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToList();
+            using var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToPooledList();
             var data = await raw.SelectAsync(async p =>
             {
                 var summary = await p.Content.GetSummary(300, 50);
@@ -86,7 +87,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             return Content(rss, ContentType.Xml);
         }
 
-        private void InsertAdvertisement(List<Item> posts, int? cid = null, string keywords = "")
+        private void InsertAdvertisement(IList<Item> posts, int? cid = null, string keywords = "")
         {
             if (posts.Count > 2)
             {
@@ -128,7 +129,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var host = Request.Host;
             var category = await categoryService.GetByIdAsync(id) ?? throw new NotFoundException("分类未找到");
             var cids = category.Flatten().Select(c => c.Id).ToArray();
-            var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && cids.Contains(p.CategoryId) && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToList();
+            using var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && cids.Contains(p.CategoryId) && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToPooledList();
             var data = await raw.SelectAsync(async p =>
             {
                 var summary = await p.Content.GetSummary(300, 50);
@@ -185,7 +186,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             string scheme = Request.Scheme;
             var host = Request.Host;
             var seminar = await seminarService.GetByIdAsync(id) ?? throw new NotFoundException("专题未找到");
-            var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && p.Seminar.Any(s => s.Id == id) && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToList();
+            using var raw = PostService.GetQuery(PostBaseWhere().And(p => p.Rss && p.Seminar.Any(s => s.Id == id) && p.Status == Status.Published && p.ModifyDate >= time), p => p.ModifyDate, false).Include(p => p.Category).AsNoTracking().Cacheable().ToPooledList();
             var data = await raw.SelectAsync(async p =>
             {
                 var summary = await p.Content.GetSummary(300, 50);

+ 1 - 2
src/Masuit.MyBlogs.Core/Controllers/SystemController.cs

@@ -12,7 +12,6 @@ using Masuit.Tools.Logging;
 using Masuit.Tools.Models;
 using Masuit.Tools.Systems;
 using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using System.ComponentModel.DataAnnotations;
 using System.Diagnostics;
@@ -357,7 +356,7 @@ namespace Masuit.MyBlogs.Core.Controllers
 
             var fs = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "whitelist.txt")).ShareReadWrite();
             string ips = await fs.ReadAllTextAsync(Encoding.UTF8, false);
-            List<string> list = ips.Split(',').Where(s => !string.IsNullOrEmpty(s)).ToList();
+            var list = ips.Split(',').Where(s => !string.IsNullOrEmpty(s)).ToList();
             list.Add(ip);
             await fs.WriteAllTextAsync(string.Join(",", list.Distinct()), Encoding.UTF8);
             CommonHelper.IPWhiteList = list;

+ 11 - 0
src/Masuit.MyBlogs.Core/Infrastructure/DataContext.cs

@@ -22,6 +22,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure
             modelBuilder.Entity<Category>().HasMany(e => e.Post).WithOne(e => e.Category).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Category>().HasMany(e => e.PostHistoryVersion).WithOne(e => e.Category).HasForeignKey(r => r.CategoryId).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Category>().HasMany(e => e.Children).WithOne(c => c.Parent).IsRequired(false).HasForeignKey(c => c.ParentId).OnDelete(DeleteBehavior.Cascade);
+            modelBuilder.Entity<Category>().Property(c => c.Path).IsRequired();
 
             modelBuilder.Entity<Post>().HasMany(e => e.Comment).WithOne(e => e.Post).HasForeignKey(r => r.PostId).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Post>().HasMany(e => e.PostHistoryVersion).WithOne(e => e.Post).HasForeignKey(r => r.PostId).OnDelete(DeleteBehavior.Cascade);
@@ -32,10 +33,20 @@ namespace Masuit.MyBlogs.Core.Infrastructure
             modelBuilder.Entity<PostHistoryVersion>().HasMany(e => e.Seminar).WithMany(s => s.PostHistoryVersion).UsingEntity(builder => builder.ToTable("SeminarPostHistoryVersion"));
 
             modelBuilder.Entity<UserInfo>().HasMany(e => e.LoginRecord).WithOne(e => e.UserInfo).OnDelete(DeleteBehavior.Cascade);
+
             modelBuilder.Entity<Menu>().HasMany(e => e.Children).WithOne(m => m.Parent).IsRequired(false).OnDelete(DeleteBehavior.Cascade);
+            modelBuilder.Entity<Menu>().Property(c => c.Path).IsRequired();
+
             modelBuilder.Entity<Comment>().HasMany(e => e.Children).WithOne(c => c.Parent).HasForeignKey(c => c.ParentId).IsRequired(false).OnDelete(DeleteBehavior.Cascade);
+            modelBuilder.Entity<Comment>().Property(c => c.Path).IsRequired();
+            modelBuilder.Entity<Comment>().Property(c => c.GroupTag).IsRequired();
+
             modelBuilder.Entity<LeaveMessage>().HasMany(e => e.Children).WithOne(c => c.Parent).HasForeignKey(c => c.ParentId).IsRequired(false).OnDelete(DeleteBehavior.Cascade);
+            modelBuilder.Entity<LeaveMessage>().Property(c => c.Path).IsRequired();
+            modelBuilder.Entity<LeaveMessage>().Property(c => c.GroupTag).IsRequired();
+
             modelBuilder.Entity<Links>().HasMany(e => e.Loopbacks).WithOne(l => l.Links).IsRequired().HasForeignKey(e => e.LinkId).OnDelete(DeleteBehavior.Cascade);
+
             modelBuilder.Entity<Advertisement>().HasMany(e => e.ClickRecords).WithOne().HasForeignKey(e => e.AdvertisementId).IsRequired().OnDelete(DeleteBehavior.Cascade);
 
             modelBuilder.Entity<Advertisement>().HasIndex(a => a.Price).HasSortOrder(SortOrder.Descending);

+ 11 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Drive/DriveAccountService.cs

@@ -7,6 +7,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
     public class DriveAccountService : IDriveAccountService
     {
         private readonly IConfidentialClientApplication _app;
+
         public DriveContext SiteContext { get; set; }
 
         /// <summary>
@@ -21,6 +22,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
             _app = tokenService.app;
             Graph = tokenService.Graph;
         }
+
         /// <summary>
         /// 返回 Oauth 验证url
         /// </summary>
@@ -30,6 +32,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
             var redirectUrl = await _app.GetAuthorizationRequestUrl(OneDriveConfiguration.Scopes).ExecuteAsync();
             return redirectUrl.AbsoluteUri;
         }
+
         /// <summary>
         /// 添加 SharePoint Site-ID 到数据库
         /// </summary>
@@ -39,6 +42,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
         public async Task AddSiteId(string siteName, string nickName)
         {
             Site site = new();
+
             //使用 Onedrive
             if (siteName == "onedrive")
             {
@@ -80,8 +84,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
 
         public List<Site> GetSites()
         {
-            List<Site> result = SiteContext.Sites.ToList();
-            return result;
+            return SiteContext.Sites.ToList();
         }
 
         /// <summary>
@@ -94,6 +97,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
             foreach (var item in SiteContext.Sites.ToArray())
             {
                 Microsoft.Graph.Drive drive;
+
                 //Onedrive
                 if (string.IsNullOrEmpty(item.SiteId))
                 {
@@ -128,12 +132,16 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
         {
             return _app.AcquireTokenSilent(OneDriveConfiguration.Scopes, OneDriveConfiguration.AccountName).ExecuteAsync().Result.AccessToken;
         }
+
         public class DriveInfo
         {
             public Microsoft.Graph.Quota Quota { get; set; }
+
             public string NickName { get; set; }
+
             public string Name { get; set; }
+
             public string[] HiddenFolders { get; set; }
         }
     }
-}
+}

+ 14 - 10
src/Masuit.MyBlogs.Core/Infrastructure/Drive/DriveService.cs

@@ -1,15 +1,14 @@
 using Masuit.MyBlogs.Core.Extensions.DriveHelpers;
 using Masuit.MyBlogs.Core.Models.Drive;
-using Masuit.Tools;
 using Microsoft.Graph;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Drive
 {
     public class DriveService : IDriveService
     {
-        readonly IDriveAccountService _accountService;
-        readonly GraphServiceClient _graph;
-        readonly DriveContext _driveContext;
+        private readonly IDriveAccountService _accountService;
+        private readonly GraphServiceClient _graph;
+        private readonly DriveContext _driveContext;
 
         public DriveService(IDriveAccountService accountService, DriveContext driveContext)
         {
@@ -17,26 +16,28 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
             _graph = accountService.Graph;
             _driveContext = driveContext;
         }
+
         /// <summary>
         /// 获取根目录的所有项目
         /// </summary>
         /// <returns></returns>
-        public async Task<List<DriveFile>> GetRootItems(string siteName = "onedrive", bool showHiddenFolders = false)
+        public async Task<List<DriveFile>> GetRootItems(string siteName, bool showHiddenFolders)
         {
             var drive = siteName != "onedrive" ? _graph.Sites[GetSiteId(siteName)].Drive : _graph.Me.Drive;
             var request = drive.Root.Children.Request();
             var result = await request.GetAsync();
             var files = await GetItems(result, siteName, showHiddenFolders);
-            files = files.OrderByDescending(f => f.CreatedTime).ToList();
-            return files;
+            return files.OrderByDescending(f => f.CreatedTime).ToList();
         }
 
         /// <summary>
         /// 根据路径获取文件夹下所有项目
         /// </summary>
         /// <param name="path"></param>
+        /// <param name="siteName"></param>
+        /// <param name="showHiddenFolders"></param>
         /// <returns></returns>
-        public async Task<List<DriveFile>> GetDriveItemsByPath(string path, string siteName = "onedrive", bool showHiddenFolders = false)
+        public async Task<List<DriveFile>> GetDriveItemsByPath(string path, string siteName, bool showHiddenFolders)
         {
             var drive = siteName != "onedrive" ? _graph.Sites[GetSiteId(siteName)].Drive : _graph.Me.Drive;
             var result = await drive.Root.ItemWithPath(path).Children.Request().GetAsync();
@@ -55,6 +56,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
             string[] imgArray = { ".png", ".jpg", ".jpeg", ".bmp", ".webp" };
             var extension = Path.GetExtension(path);
             var drive = siteName != "onedrive" ? _graph.Sites[GetSiteId(siteName)].Drive : _graph.Me.Drive;
+
             //这么写是因为:分块上传图片后直接获取会报错。
             if (imgArray.Contains(extension))
             {
@@ -84,6 +86,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
         }
 
         #region PrivateMethod
+
         private DriveFile GetItem(DriveItem result)
         {
             var file = new DriveFile()
@@ -168,6 +171,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
 
             return downloadUrl;
         }
-        #endregion
+
+        #endregion PrivateMethod
     }
-}
+}

+ 4 - 2
src/Masuit.MyBlogs.Core/Infrastructure/Drive/IDriveAccountService.cs

@@ -4,13 +4,14 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
 {
     public interface IDriveAccountService
     {
-
         public DriveContext SiteContext { get; set; }
+
         /// <summary>
         /// 返回 Oauth 验证url
         /// </summary>
         /// <returns></returns>
         public Task<string> GetAuthorizationRequestUrl();
+
         /// <summary>
         /// 添加 SharePoint Site-ID
         /// </summary>
@@ -24,6 +25,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
         /// </summary>
         /// <value></value>
         public Microsoft.Graph.GraphServiceClient Graph { get; set; }
+
         /// <summary>
         /// 返回所有 sharepoint site
         /// </summary>
@@ -49,4 +51,4 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
         /// <returns></returns>
         public string GetToken();
     }
-}
+}

+ 4 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Drive/IDriveService.cs

@@ -5,8 +5,11 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Drive
     public interface IDriveService
     {
         public Task<List<DriveFile>> GetRootItems(string siteName, bool showHiddenFolders);
+
         public Task<List<DriveFile>> GetDriveItemsByPath(string path, string siteName, bool showHiddenFolders);
+
         public Task<DriveFile> GetDriveItemByPath(string path, string siteName);
+
         public Task<string> GetUploadUrl(string path, string siteName = "onedrive");
     }
-}
+}

+ 15 - 14
src/Masuit.MyBlogs.Core/Infrastructure/Repository/BaseRepository.cs

@@ -1,5 +1,6 @@
 using AutoMapper;
 using AutoMapper.QueryableExtensions;
+using Collections.Pooled;
 using EFCoreSecondLevelCacheInterceptor;
 using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
@@ -73,9 +74,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// </summary>
         /// <typeparam name="TDto">映射实体</typeparam>
         /// <returns></returns>
-        public virtual List<TDto> GetAllFromCache<TDto>() where TDto : class
+        public virtual PooledList<TDto> GetAllFromCache<TDto>() where TDto : class
         {
-            return DataContext.Set<T>().ProjectTo<TDto>(MapperConfig).Cacheable().ToList();
+            return DataContext.Set<T>().ProjectTo<TDto>(MapperConfig).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -119,9 +120,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// <param name="orderby">排序字段</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        public virtual List<T> GetAllFromCache<TS>(Expression<Func<T, TS>> orderby, bool isAsc = true)
+        public virtual PooledList<T> GetAllFromCache<TS>(Expression<Func<T, TS>> orderby, bool isAsc = true)
         {
-            return GetAllNoTracking(orderby, isAsc).Cacheable().ToList();
+            return GetAllNoTracking(orderby, isAsc).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -157,9 +158,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// <param name="orderby">排序字段</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns>还未执行的SQL语句</returns>
-        public virtual List<TDto> GetAllFromCache<TS, TDto>(Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class
+        public virtual PooledList<TDto> GetAllFromCache<TS, TDto>(Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class
         {
-            return GetAll(orderby, isAsc).ProjectTo<TDto>(MapperConfig).Cacheable().ToList();
+            return GetAll(orderby, isAsc).ProjectTo<TDto>(MapperConfig).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -203,9 +204,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// </summary>
         /// <param name="where">查询条件</param>
         /// <returns></returns>
-        public virtual List<T> GetQueryFromCache(Expression<Func<T, bool>> @where)
+        public virtual PooledList<T> GetQueryFromCache(Expression<Func<T, bool>> where)
         {
-            return DataContext.Set<T>().Where(where).AsNoTracking().Cacheable().ToList();
+            return DataContext.Set<T>().Where(where).AsNoTracking().Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -226,9 +227,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// <param name="orderby">排序方式</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        public virtual List<T> GetQueryFromCache<TS>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true)
+        public virtual PooledList<T> GetQueryFromCache<TS>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true)
         {
-            return GetQueryNoTracking(where, orderby, isAsc).Cacheable().ToList();
+            return GetQueryNoTracking(where, orderby, isAsc).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -310,9 +311,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// </summary>
         /// <param name="where">查询条件</param>
         /// <returns>实体集合</returns>
-        public virtual List<TDto> GetQueryFromCache<TDto>(Expression<Func<T, bool>> where) where TDto : class
+        public virtual PooledList<TDto> GetQueryFromCache<TDto>(Expression<Func<T, bool>> where) where TDto : class
         {
-            return DataContext.Set<T>().Where(where).ProjectTo<TDto>(MapperConfig).Cacheable().ToList();
+            return DataContext.Set<T>().Where(where).ProjectTo<TDto>(MapperConfig).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -334,9 +335,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// <param name="orderby">排序方式</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        public virtual List<TDto> GetQueryFromCache<TS, TDto>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class
+        public virtual PooledList<TDto> GetQueryFromCache<TS, TDto>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class
         {
-            return GetQuery(where, orderby, isAsc).ProjectTo<TDto>(MapperConfig).Cacheable().ToList();
+            return GetQuery(where, orderby, isAsc).ProjectTo<TDto>(MapperConfig).Cacheable().ToPooledList();
         }
 
         /// <summary>

+ 9 - 8
src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs

@@ -2,6 +2,7 @@
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.Tools.Models;
 using System.Linq.Expressions;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
 {
@@ -43,7 +44,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// </summary>
         /// <typeparam name="TDto">映射实体</typeparam>
         /// <returns></returns>
-        List<TDto> GetAllFromCache<TDto>() where TDto : class;
+        PooledList<TDto> GetAllFromCache<TDto>() where TDto : class;
 
         /// <summary>
         /// 从二级缓存获取所有实体
@@ -77,7 +78,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <param name="orderby">排序字段</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        List<T> GetAllFromCache<TS>(Expression<Func<T, TS>> @orderby, bool isAsc = true);
+        PooledList<T> GetAllFromCache<TS>(Expression<Func<T, TS>> orderby, bool isAsc = true);
 
         /// <summary>
         /// 从二级缓存获取所有实体
@@ -106,7 +107,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <param name="orderby">排序字段</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        List<TDto> GetAllFromCache<TS, TDto>(Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
+        PooledList<TDto> GetAllFromCache<TS, TDto>(Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class;
 
         /// <summary>
         /// 从二级缓存获取所有实体
@@ -159,7 +160,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// </summary>
         /// <param name="where">查询条件</param>
         /// <returns></returns>
-        List<T> GetQueryFromCache(Expression<Func<T, bool>> @where);
+        PooledList<T> GetQueryFromCache(Expression<Func<T, bool>> where);
 
         /// <summary>
         /// 基本查询方法,获取一个集合,优先从二级缓存读取
@@ -176,7 +177,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <param name="orderby">排序方式</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        List<T> GetQueryFromCache<TS>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true);
+        PooledList<T> GetQueryFromCache<TS>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true);
 
         /// <summary>
         /// 基本查询方法,获取一个集合,优先从二级缓存读取
@@ -193,7 +194,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// </summary>
         /// <param name="where">查询条件</param>
         /// <returns></returns>
-        List<TDto> GetQueryFromCache<TDto>(Expression<Func<T, bool>> @where) where TDto : class;
+        PooledList<TDto> GetQueryFromCache<TDto>(Expression<Func<T, bool>> where) where TDto : class;
 
         /// <summary>
         /// 基本查询方法,获取一个被AutoMapper映射后的集合,优先从二级缓存读取
@@ -211,7 +212,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <param name="orderby">排序方式</param>
         /// <param name="isAsc">是否升序</param>
         /// <returns></returns>
-        List<TDto> GetQueryFromCache<TS, TDto>(Expression<Func<T, bool>> @where, Expression<Func<T, TS>> @orderby, bool isAsc = true) where TDto : class;
+        PooledList<TDto> GetQueryFromCache<TS, TDto>(Expression<Func<T, bool>> where, Expression<Func<T, TS>> orderby, bool isAsc = true) where TDto : class;
 
         /// <summary>
         /// 基本查询方法,获取一个被AutoMapper映射后的集合,优先从二级缓存读取
@@ -644,7 +645,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
 
         T this[int id] => GetById(id);
 
-        List<T> this[Expression<Func<T, bool>> where] => GetQuery(where).ToList();
+        PooledList<T> this[Expression<Func<T, bool>> where] => GetQuery(where).ToPooledList();
     }
 
     public partial interface ICategoryRepository : IBaseRepository<Category>

+ 1 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/ISearchDetailsRepository.cs

@@ -11,4 +11,4 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
         /// <returns></returns>
         List<SearchRank> GetRanks(DateTime start);
     }
-}
+}

+ 4 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Repository/PostRepository.cs

@@ -3,6 +3,7 @@ using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Microsoft.EntityFrameworkCore;
 using System.Linq.Expressions;
+using Collections.Pooled;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Repository
 {
@@ -34,9 +35,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         /// </summary>
         /// <param name="where">查询条件</param>
         /// <returns>还未执行的SQL语句</returns>
-        public override List<Post> GetQueryFromCache(Expression<Func<Post, bool>> @where)
+        public override PooledList<Post> GetQueryFromCache(Expression<Func<Post, bool>> where)
         {
-            return DataContext.Post.Include(p => p.Category).Where(where).Cacheable().ToList();
+            return DataContext.Post.Include(p => p.Category).Where(where).Cacheable().ToPooledList();
         }
 
         /// <summary>
@@ -52,4 +53,4 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
             return isAsc ? DataContext.Post.Include(p => p.Category).Where(where).OrderBy(orderby) : DataContext.Post.Include(p => p.Category).Where(where).OrderByDescending(orderby);
         }
     }
-}
+}

+ 1 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Repository/SearchDetailsRepository.cs

@@ -26,4 +26,4 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
             return DataContext.SearchDetails.Where(s => s.SearchTime > start).Select(s => new { s.IP, s.Keywords }).Distinct().GroupBy(s => s.Keywords).Select(g => new SearchRank { Keywords = g.Key, Count = g.Count() }).OrderByDescending(s => s.Count).Take(30).ToList();
         }
     }
-}
+}

+ 12 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IBaseService.cs

@@ -1,4 +1,5 @@
-using Masuit.LuceneEFCore.SearchEngine;
+using Collections.Pooled;
+using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.Tools.Models;
 using System.Linq.Expressions;
 
@@ -735,15 +736,23 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         Task<IEnumerable<T>> AddEntitiesAsync(IList<T> list);
 
         T this[int id] => GetById(id);
+
         string this[int id, Expression<Func<T, string>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         int this[int id, Expression<Func<T, int>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         DateTime this[int id, Expression<Func<T, DateTime>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         long this[int id, Expression<Func<T, long>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
+
         decimal this[int id, Expression<Func<T, decimal>> selector] => GetQuery(t => t.Id == id).Select(selector).FirstOrDefault();
 
-        List<T> this[Expression<Func<T, bool>> where] => GetQuery(where).ToList();
+        PooledList<T> this[Expression<Func<T, bool>> where] => GetQuery(where).ToPooledList();
+
         public static T operator +(IBaseService<T> left, T right) => left.AddEntitySaved(right);
+
         public static bool operator -(IBaseService<T> left, T right) => left.DeleteEntitySaved(right);
+
         public static bool operator -(IBaseService<T> left, int id) => left.DeleteById(id);
     }
-}
+}

+ 1 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IPostService.cs

@@ -33,6 +33,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         /// <param name="keyword"></param>
         Task Highlight(Post p, string keyword);
 
-        void SolvePostsCategory(List<PostDto> posts);
+        void SolvePostsCategory(IList<PostDto> posts);
     }
 }

+ 1 - 2
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/ISearchDetailsService.cs

@@ -4,7 +4,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 {
     public partial interface ISearchDetailsService : IBaseService<SearchDetails>
     {
-
         /// <summary>
         /// 搜索统计
         /// </summary>
@@ -12,4 +11,4 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         /// <returns></returns>
         List<SearchRank> GetRanks(DateTime start);
     }
-}
+}

+ 4 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -3,6 +3,7 @@ using AngleSharp.Dom;
 using AngleSharp.Html.Parser;
 using AutoMapper;
 using CacheManager.Core;
+using Collections.Pooled;
 using EFCoreSecondLevelCacheInterceptor;
 using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.LuceneEFCore.SearchEngine.Interfaces;
@@ -97,7 +98,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             var result = _cacheManager.GetOrAdd(cacheKey, _ =>
             {
                 var searchResult = SearchEngine.ScoredSearch<Post>(BuildSearchOptions(page, size, keyword));
-                var entities = searchResult.Results.Where(s => s.Entity.Status == Status.Published).DistinctBy(s => s.Entity.Id).ToList();
+                using var entities = searchResult.Results.Where(s => s.Entity.Status == Status.Published).DistinctBy(s => s.Entity.Id).ToPooledList();
                 var ids = entities.Select(s => s.Entity.Id).ToArray();
                 var dic = GetQuery<PostDto>(p => ids.Contains(p.Id)).ToDictionary(p => p.Id);
                 var posts = entities.Where(s => dic.ContainsKey(s.Entity.Id)).Select(s => dic[s.Entity.Id]).ToList();
@@ -116,7 +117,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
             return result;
         }
 
-        public void SolvePostsCategory(List<PostDto> posts)
+        public void SolvePostsCategory(IList<PostDto> posts)
         {
             var cids = posts.Select(p => p.CategoryId).Distinct().ToArray();
             var categories = _categoryRepository.GetQuery(c => cids.Contains(c.Id)).Include(c => c.Parent).Cacheable().ToDictionary(c => c.Id);
@@ -129,7 +130,7 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
         /// <param name="posts"></param>
         /// <param name="keywords"></param>
         /// <param name="highlighter"></param>
-        private static void HighlightSegment(List<PostDto> posts, List<string> keywords, Highlighter highlighter)
+        private static void HighlightSegment(IList<PostDto> posts, List<string> keywords, Highlighter highlighter)
         {
             foreach (var p in posts)
             {

+ 1 - 0
src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj

@@ -46,6 +46,7 @@
         <PackageReference Include="CacheManager.StackExchange.Redis" Version="1.2.0" />
         <PackageReference Include="CHTCHSConv" Version="1.0.0" />
         <PackageReference Include="CLRStats" Version="1.0.0" />
+        <PackageReference Include="Collections.Pooled" Version="1.0.82" />
         <PackageReference Include="CSRedisCore" Version="3.6.9" />
         <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.4.0" />
         <PackageReference Include="Hangfire" Version="1.7.29" />

+ 18 - 5
src/Masuit.MyBlogs.Core/Models/ViewModel/HomePageViewModel.cs

@@ -1,13 +1,15 @@
-using Masuit.MyBlogs.Core.Models.DTO;
+using Collections.Pooled;
+using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.Tools.Models;
+using Masuit.Tools.Systems;
 
 namespace Masuit.MyBlogs.Core.Models.ViewModel
 {
     /// <summary>
     /// 首页视图模型
     /// </summary>
-    public class HomePageViewModel
+    public class HomePageViewModel : Disposable
     {
         /// <summary>
         /// 文章列表
@@ -22,7 +24,7 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// <summary>
         /// 分类列表
         /// </summary>
-        public List<CategoryDto_P> Categories { get; set; }
+        public PooledList<CategoryDto_P> Categories { get; set; }
 
         /// <summary>
         /// 标签列表
@@ -32,12 +34,12 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// <summary>
         /// 近期热搜
         /// </summary>
-        public List<KeywordsRank> HotSearch { get; set; }
+        public PooledList<KeywordsRank> HotSearch { get; set; }
 
         /// <summary>
         /// 热门文章
         /// </summary>
-        public List<PostDto> Top5Post { get; set; }
+        public PooledList<PostDto> Top5Post { get; set; }
 
         /// <summary>
         /// 文章列表查询
@@ -63,5 +65,16 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// 分页参数
         /// </summary>
         public Pagination PageParams { get; set; }
+
+        /// <summary>
+        /// 释放
+        /// </summary>
+        /// <param name="disposing"></param>
+        public override void Dispose(bool disposing)
+        {
+            Categories.Dispose();
+            HotSearch.Dispose();
+            Top5Post.Dispose();
+        }
     }
 }

+ 2 - 1
src/Masuit.MyBlogs.Core/PrepareStartup.cs

@@ -21,6 +21,7 @@ using Microsoft.Net.Http.Headers;
 using StackExchange.Profiling;
 using System.Text.RegularExpressions;
 using System.Web;
+using Collections.Pooled;
 using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode;
 
 namespace Masuit.MyBlogs.Core
@@ -46,7 +47,7 @@ namespace Masuit.MyBlogs.Core
                 double time = HiPerfTimer.Execute(() =>
                 {
                     var db = app.ApplicationServices.GetRequiredService<DataContext>();
-                    var set = db.Post.Select(p => $"{p.Title},{p.Label},{p.Keyword}").AsParallel().SelectMany(s => Regex.Split(s, @"\p{P}(?<!\.|#)|\p{Z}|\p{S}")).Where(s => s.Length > 1).ToHashSet();
+                    using var set = db.Post.Select(p => $"{p.Title},{p.Label},{p.Keyword}").AsParallel().SelectMany(s => Regex.Split(s, @"\p{P}(?<!\.|#)|\p{Z}|\p{S}")).Where(s => s.Length > 1).ToPooledSet();
                     var lines = File.ReadAllLines(Path.Combine(env.ContentRootPath, "App_Data", "CustomKeywords.txt")).Union(set);
                     KeywordsManager.AddWords(lines);
                     KeywordsManager.AddSynonyms(File.ReadAllLines(Path.Combine(env.ContentRootPath, "App_Data", "CustomSynonym.txt")).Where(s => s.Contains(" ")).Select(s =>

+ 2 - 1
src/Masuit.MyBlogs.Core/Views/Dashboard/Counter.razor

@@ -4,6 +4,7 @@
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.Tools
 @using System.IO
+@using Collections.Pooled
 @using Masuit.Tools.Logging
 @using PerformanceCounter = Masuit.MyBlogs.Core.Common.PerformanceCounter
 @inject IJSRuntime JS;
@@ -123,7 +124,7 @@
     private readonly CpuInfo cpu = SystemInfo.GetCpuInfo()[0];
     private readonly RamInfo memory = SystemInfo.GetRamInfo();
     private readonly IList<string> macs = SystemInfo.GetMacAddress();
-    private readonly IList<string> ips = SystemInfo.GetLocalIPs().OrderBy(u => u.Address.AddressFamily != AddressFamily.InterNetwork).Select(a => a.Address.ToString()).ToList();
+    private readonly IList<string> ips = SystemInfo.GetLocalIPs().OrderBy(u => u.Address.AddressFamily != AddressFamily.InterNetwork).Select(a => a.Address.ToString()).ToPooledList();
     private readonly DriveInfo[] _driveInfos = DriveInfo.GetDrives().Where(d => d.IsReady).ToArray();
 
     protected override void OnInitialized()

+ 9 - 8
src/Masuit.MyBlogs.Core/Views/Home/Category.cshtml

@@ -7,6 +7,7 @@
 @using Masuit.MyBlogs.Core.Models.Enum
 @using EFCoreSecondLevelCacheInterceptor
 @using Masuit.Tools.Models
+@using Collections.Pooled
 @model Masuit.MyBlogs.Core.Models.ViewModel.HomePageViewModel
 @inject ICategoryService CategoryService
 @{
@@ -14,24 +15,24 @@
     ViewBag.Title = "分类_" + cat.Path();
     Layout = "~/Views/Shared/_Layout.cshtml";
     var level = cat.Level();
-    List<Category> children2 = new List<Category>();
-    List<Category> children3 = new List<Category>();
+    using var children2 = new PooledList<Category>();
+    using var children3 = new PooledList<Category>();
     var parentId = cat.ParentId;
     switch (level) {
         case 1:
-            children2.AddRange(cat.Children.Where(c => c.Status == Status.Available).OrderBy(c => c.Id).ToList());
+            children2.AddRange(cat.Children.Where(c => c.Status == Status.Available).OrderBy(c => c.Id).ToPooledList());
             break;
         case 2:
-            children2.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == parentId, c => c.Name).Cacheable().ToList());
-            children3.AddRange(cat.Children.Where(c => c.Status == Status.Available).OrderBy(c => c.Id).ToList());
+            children2.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == parentId, c => c.Name).Cacheable().ToPooledList());
+            children3.AddRange(cat.Children.Where(c => c.Status == Status.Available).OrderBy(c => c.Id).ToPooledList());
             break;
         case 3:
             var topid = cat.Parent.ParentId;
-            children2.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == topid, c => c.Name).Cacheable().ToList());
-            children3.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == parentId, c => c.Name).Cacheable().ToList());
+            children2.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == topid, c => c.Name).Cacheable().ToPooledList());
+            children3.AddRange(CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == parentId, c => c.Name).Cacheable().ToPooledList());
             break;
     }
-    var alllist = CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == null, c => c.Name).Select(c => new{c.Id,c.Name}).Cacheable().ToList();
+    var alllist = CategoryService.GetQuery(c => c.Status == Status.Available && c.ParentId == null, c => c.Name).Select(c => new{c.Id,c.Name}).Cacheable().ToPooledList();
 }
 <style>
     .bg-title {

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Home/Index.cshtml

@@ -58,7 +58,7 @@
 @if (Model.Notices.Any())
 {
     <div class="container notice">
-        <a asp-controller="Notice" asp-action="Details" asp-route-id="@(Model.Notices.FirstOrDefault()?.Id)">
+        <a asp-controller="Notice" asp-action="Details" asp-route-id="@(Model.Notices[0].Id)">
             <h3 class="size18 text-red text-center">网站最新公告</h3>
         </a>
         @Html.Raw(Model.Notices[0].Content.Replace("img src=", $"img class='lazyload' title='{CommonHelper.SystemSettings["Title"]}' alt='{CommonHelper.SystemSettings["Title"]}' decoding='async' loading='lazy' data-src="))

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Post/CompareVersion.cshtml

@@ -9,7 +9,7 @@
 	ViewBag.Title = Model[0].Title + "版本对比";
 	Layout = "~/Views/Shared/_Layout.cshtml";
 	string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
-	List<Advertisement> ads = ViewBag.Ads;
+	IList<Advertisement> ads = ViewBag.Ads;
 }
 <style>
 	ins {

+ 5 - 5
src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml

@@ -88,7 +88,7 @@
 									@{
 										await Html.RenderPartialAsync("CategoryPath", Model.Category);
 									}
-									| 评论总数:<span class="text-danger">@Model.Comment.Count</span>条 | @(await Html.RenderComponentAsync<PostOnline>(RenderMode.ServerPrerendered, new{ Model.Id,IP=Context.Connection.RemoteIpAddress.ToString()}))
+									| 评论总数:<span class="text-danger">@ViewBag.CommentsCount</span>条 | @(await Html.RenderComponentAsync<PostOnline>(RenderMode.ServerPrerendered, new{ Model.Id,IP=Context.Connection.RemoteIpAddress.ToString()}))
 									@if (Model.Seminar.Any())
 									{
 										<span> | 所属专题:</span>
@@ -183,12 +183,12 @@
 						}
 					</div>
 				</section>
-				@if (Model.PostHistoryVersion.Any())
+				@if (ViewBag.HistoryCount>0)
 				{
 					<section class="wow margintop20 animated fadeIn">
 						<h3>文章历史版本:</h3>
 						<p>
-							修改次数:@Model.PostHistoryVersion.Count 次
+							修改次数:@ViewBag.HistoryCount 次
 							<a asp-controller="Post" asp-action="History" asp-route-id="@Model.Id">查看历史版本</a>
 						</p>
 					</section>
@@ -281,7 +281,7 @@
 							<h4>本篇文章已禁用评论和回复功能</h4>
 						}
 						<ul class="wow media-list"></ul>
-						@if (Model.Comment.Any(c => c.Status == Status.Published))
+						@if (ViewBag.CommentsCount>0)
 						{
 							<div class="row">
 								<div class="col-md-12 text-center">
@@ -355,7 +355,7 @@
 		loading();
 		$('#pageToolbar').Paging({ //异步加载评论
 			pagesize: 10,
-			count: @Model.Comment.Count(c =>c.ParentId==0&& c.Status==Status.Published),
+			count: @ViewBag.CommentsCount,
 			toolbar: true,
 			callback: function(page, size, count) {
 				window.get("/comment/getcomments?id=" [email protected]+"&page=" + page + "&size=" + size, function(data) {

+ 5 - 5
src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml

@@ -88,7 +88,7 @@
 									@{
 										await Html.RenderPartialAsync("CategoryPath", Model.Category);
 									}
-									| 评论总数:<span class="text-danger">@Model.Comment.Count</span>条 | 热度:<span class="text-danger">@Model.TotalViewCount</span>℃ | @(await Html.RenderComponentAsync<PostOnline>(RenderMode.ServerPrerendered, new { Model.Id,IP=Context.Connection.RemoteIpAddress.ToString(),IsAdmin=true })) | 状态:@Model.Status.GetDisplay()
+									| 评论总数:<span class="text-danger">@ViewBag.CommentsCount</span>条 | 热度:<span class="text-danger">@Model.TotalViewCount</span>℃ | @(await Html.RenderComponentAsync<PostOnline>(RenderMode.ServerPrerendered, new { Model.Id,IP=Context.Connection.RemoteIpAddress.ToString(),IsAdmin=true })) | 状态:@Model.Status.GetDisplay()
 									@if (Model.Seminar.Any())
 									{
 										<span> | 所属专题:</span>
@@ -185,12 +185,12 @@
 						}
 					</div>
 				</section>
-				@if (Model.PostHistoryVersion.Any())
+				@if (ViewBag.HistoryCount>0)
 				{
 					<section class="wow margintop20 animated fadeIn">
 						<h3>文章历史版本:</h3>
 						<p>
-							修改次数:@Model.PostHistoryVersion.Count 次
+							修改次数:@ViewBag.HistoryCount 次
 							<a asp-controller="Post" asp-action="History" asp-route-id="@Model.Id">查看历史版本</a>
 						</p>
 					</section>
@@ -249,7 +249,7 @@
 							</div>
 						</form>
 						<ul class="wow media-list"></ul>
-						@if (Model.Comment.Any(c => c.Status == Status.Published))
+						@if (ViewBag.CommentsCount>0)
 						{
 							<div class="row">
 								<div class="col-md-12 text-center">
@@ -309,7 +309,7 @@
 		loading();
 		$('#pageToolbar').Paging({ //异步加载评论
 			pagesize: 10,
-			count: @Model.Comment.Count(c =>c.ParentId==0&& c.Status==Status.Published),
+			count: @ViewBag.CommentsCount,
 			toolbar: true,
 			callback: function(page, size, count) {
 				window.get("/comment/getcomments?id=" [email protected]+"&page=" + page + "&size=" + size, function (data) {

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml

@@ -8,8 +8,8 @@
 @{
 	ViewBag.Title = "站内搜索:" + ViewBag.Keyword;
 	Layout = "~/Views/Shared/_Layout.cshtml";
-	List<KeywordsRank> hotSearches = ViewBag.hotSearches;
-	List<string> relateKeywords = ViewBag.RelateKeywords;
+	IList<KeywordsRank> hotSearches = ViewBag.hotSearches;
+	IList<string> relateKeywords = ViewBag.RelateKeywords;
 	Advertisement ad = ViewBag.Ads;
 }
 <div class="container min-height610">

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml

@@ -60,7 +60,7 @@
 				<div class="col-md-6">
 					@for (var i = 0; i < tags.Length; i++)
 					{
-						<a asp-controller="Home" asp-action="Tag" asp-route-tag="@tags[i]" target="_blank">
+						<a asp-controller="Home" asp-action="Tag" asp-route-tag="@tags[i]">
 							<span class="label label-@colors[i]">@tags[i]</span>
 						</a>
 					}

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml

@@ -62,7 +62,7 @@
 				<div class="col-md-5">
 					@for (var i = 0; i < tags.Length; i++)
 					{
-						<a asp-controller="Home" asp-action="Tag" asp-route-tag="@tags[i]" target="_blank">
+						<a asp-controller="Home" asp-action="Tag" asp-route-tag="@tags[i]">
 							<span class="label label-@colors[i]">@tags[i]</span>
 						</a>
 					}

+ 3 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial.cshtml

@@ -2,6 +2,8 @@
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 @using Masuit.MyBlogs.Core.Models.Enum
+@using Microsoft.AspNetCore.Mvc.TagHelpers
+@using Collections.Pooled
 @model Masuit.MyBlogs.Core.Models.ViewModel.HomePageViewModel
 @inject IPostService PostService
 
@@ -23,7 +25,7 @@
         }
         else
         {
-            var list = Model.PostsQueryable.OrderByRandom().Take(5).ToList();
+            using var list = Model.PostsQueryable.OrderByRandom().Take(5).ToPooledList();
             if (list.Any())
             {
                 if (Model.Posts.CurrentPage == 1)

+ 3 - 1
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial_Admin.cshtml

@@ -3,6 +3,8 @@
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 @using Masuit.MyBlogs.Core.Models.Enum
+@using Microsoft.AspNetCore.Mvc.TagHelpers
+@using Collections.Pooled
 @model Masuit.MyBlogs.Core.Models.ViewModel.HomePageViewModel
 @inject IPostService PostService
 
@@ -24,7 +26,7 @@
         }
         else
         {
-            var list = Model.PostsQueryable.OrderByRandom().Take(5).ToList();
+            using var list = Model.PostsQueryable.OrderByRandom().Take(5).ToPooledList();
             if (list.Any())
             {
                 <div class="page-header">

+ 5 - 2
src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml

@@ -12,11 +12,14 @@
 @using EFCoreSecondLevelCacheInterceptor
 @using Masuit.Tools.Models
 @using Microsoft.AspNetCore.Http.Extensions
+@using Microsoft.AspNetCore.Mvc.TagHelpers
+@using StackExchange.Profiling
+@using Collections.Pooled
 @{
     string[] colors = { "success", "info", "warning", "danger", "default" };
-    List<Menu> menus = _menuService.GetQueryFromCache(m => m.Status == Status.Available,m => m.Sort).ToList().ToTree(m => m.Id,m => m.ParentId);
+    List<Menu> menus = _menuService.GetQueryFromCache(m => m.Status == Status.Available,m => m.Sort).ToPooledList().ToTree(m => m.Id,m => m.ParentId);
     var user = Context.Session.Get<UserInfoDto>(SessionKey.UserInfo) ?? new UserInfoDto();
-    var links = _linksService.GetQuery(l => l.Status == Status.Available).OrderByDescending(l => l.Recommend).ThenByDescending(l => l.Loopbacks.GroupBy(x => x.IP).Count()).Take(30).Select(e => new{e.Url,e.Name}).Cacheable().ToList();
+    using var links = _linksService.GetQuery(l => l.Status == Status.Available).OrderByDescending(l => l.Recommend).ThenByDescending(l => l.Loopbacks.GroupBy(x => x.IP).Count()).Take(30).Select(e => new{e.Url,e.Name}).Cacheable().ToPooledList();
 }
 
 <!DOCTYPE html>