diff --git a/src/TerribleDev.Blog.Web/Factories/BlogCacheFactory.cs b/src/TerribleDev.Blog.Web/Factories/BlogCacheFactory.cs index cae58be..377ce9e 100644 --- a/src/TerribleDev.Blog.Web/Factories/BlogCacheFactory.cs +++ b/src/TerribleDev.Blog.Web/Factories/BlogCacheFactory.cs @@ -14,7 +14,7 @@ namespace TerribleDev.Blog.Web.Factories public static PostCache ProjectPostCache(IEnumerable rawPosts) { var orderedPosts = rawPosts.OrderByDescending(a => a.PublishDate); - var posts = new List(); + var posts = new List(orderedPosts); var urlToPosts = new Dictionary(); var tagsToPost = new Dictionary>(); var postsByPage = new Dictionary>(); diff --git a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs index 32ecad3..204e75a 100644 --- a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs +++ b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs @@ -17,25 +17,21 @@ namespace TerribleDev.Blog.Web { public class BlogFactory { - public IEnumerable GetAllPosts(string domain) + public async Task> GetAllPostsAsync(string domain) { // why didn't I use f# I'd have a pipe operator by now var posts = GetPosts(); - var list = new ConcurrentBag(); - Parallel.ForEach(posts, post => - { - var (text, fileInfo) = GetFileText(post); - list.Add(ParsePost(text, fileInfo.Name, domain)); - }); - return list; + return await Task.WhenAll(posts.Select(async (post) => { + var (text, fileInfo) = await GetFileText(post); + return ParsePost(text, fileInfo.Name, domain); + })); } - private static (string text, FileInfo fileInfo) GetFileText(string filePath) + private static async Task<(string text, FileInfo fileInfo)> GetFileText(string filePath) { var fileInfo = new FileInfo(filePath); - var text = File.ReadAllText(fileInfo.FullName); + var text = await File.ReadAllTextAsync(fileInfo.FullName); return (text, fileInfo); - } public IEnumerable GetPosts() => Directory.EnumerateFiles(Path.Combine(Directory.GetCurrentDirectory(), "Posts"), "*.md", SearchOption.TopDirectoryOnly); @@ -46,13 +42,7 @@ namespace TerribleDev.Blog.Web return serializer.Deserialize(ymlText); } - public IPost ParsePost(string postText, string fileName, string domain) - { - var splitFile = postText.Split("---"); - var ymlRaw = splitFile[0]; - var markdownText = string.Join("", splitFile.Skip(1)); - var postSettings = ParseYaml(ymlRaw); - var resolvedUrl = !string.IsNullOrWhiteSpace(postSettings.permalink) ? postSettings.permalink : fileName.Split('.')[0].Replace(' ', '-').WithoutSpecialCharacters(); + public (string postContent, string postContentPlain, string summary, string postSummaryPlain, IList postImages) ResolveContentForPost(string markdownText, string fileName, string resolvedUrl, string domain) { List postImages = new List(); var pipeline = new MarkdownPipelineBuilder() .Use(new AbsoluteLinkConverter(resolvedUrl, domain)) @@ -67,6 +57,15 @@ namespace TerribleDev.Blog.Web var summary = postContent.Split("")[0]; var postSummaryPlain = postContentPlain.Split("")[0]; + return (postContent, postContentPlain, summary, postSummaryPlain, postImages); + } + public IPost ParsePost(string postText, string fileName, string domain) + { + var splitFile = postText.Split("---"); + var ymlRaw = splitFile[0]; + var markdownText = string.Join("", splitFile.Skip(1)); + var postSettings = ParseYaml(ymlRaw); + var resolvedUrl = !string.IsNullOrWhiteSpace(postSettings.permalink) ? postSettings.permalink : fileName.Split('.')[0].Replace(' ', '-').WithoutSpecialCharacters(); return new Post() { @@ -76,12 +75,17 @@ namespace TerribleDev.Blog.Web RelativeUrl = $"/{resolvedUrl}/", CanonicalUrl = $"https://blog.terrible.dev/{resolvedUrl}/", UrlWithoutPath = resolvedUrl, - Content = new HtmlString(postContent), - Summary = new HtmlString(summary), - SummaryPlain = postSummaryPlain, - SummaryPlainShort = (postContentPlain.Length <= 147 ? postContentPlain : postContentPlain.Substring(0, 146)) + "...", - ContentPlain = postContentPlain, - Images = postImages.Distinct().ToList() + Content = new Lazy(() => { + (string postContent, string postContentPlain, string summary, string postSummaryPlain, IList postImages) = ResolveContentForPost(markdownText, fileName, resolvedUrl, domain); + return new PostContent() { + Content = new HtmlString(postContent), + Images = postImages, + ContentPlain = postContent, + Summary = new HtmlString(summary), + SummaryPlain = postSummaryPlain, + SummaryPlainShort = (postContentPlain.Length <= 147 ? postContentPlain : postContentPlain.Substring(0, 146)) + "..." + }; + }), }; } } diff --git a/src/TerribleDev.Blog.Web/Factories/LazyBlogFactory.cs b/src/TerribleDev.Blog.Web/Factories/LazyBlogFactory.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/TerribleDev.Blog.Web/Models/IPost.cs b/src/TerribleDev.Blog.Web/Models/IPost.cs index 54be954..a2d2796 100644 --- a/src/TerribleDev.Blog.Web/Models/IPost.cs +++ b/src/TerribleDev.Blog.Web/Models/IPost.cs @@ -13,14 +13,9 @@ namespace TerribleDev.Blog.Web.Models string UrlWithoutPath { get; set; } string RelativeUrl { get; set; } string Title { get; set; } - HtmlString Summary { get; set; } DateTime PublishDate { get; set; } - HtmlString Content { get; set; } - string ContentPlain { get; set; } - string SummaryPlain { get; set; } - string SummaryPlainShort { get; set; } IList tags { get; set; } - IList Images { get; set;} + Lazy Content { get; set; } } } diff --git a/src/TerribleDev.Blog.Web/Models/IPostContent.cs b/src/TerribleDev.Blog.Web/Models/IPostContent.cs new file mode 100644 index 0000000..a60df76 --- /dev/null +++ b/src/TerribleDev.Blog.Web/Models/IPostContent.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Html; + +namespace TerribleDev.Blog.Web.Models +{ + public interface IPostContent + { + HtmlString Content { get; set; } + HtmlString Summary { get; set; } + string ContentPlain { get; set; } + string SummaryPlain { get; set; } + string SummaryPlainShort { get; set; } + IList Images { get; set; } + } +} diff --git a/src/TerribleDev.Blog.Web/Models/Post.cs b/src/TerribleDev.Blog.Web/Models/Post.cs index 676c596..1731935 100644 --- a/src/TerribleDev.Blog.Web/Models/Post.cs +++ b/src/TerribleDev.Blog.Web/Models/Post.cs @@ -14,12 +14,7 @@ namespace TerribleDev.Blog.Web.Models public string RelativeUrl { get; set; } public string Title { get; set; } public DateTime PublishDate { get; set; } - public HtmlString Content { get; set; } - public HtmlString Summary { get; set; } - public string ContentPlain { get; set; } - public string SummaryPlain { get; set; } - public string SummaryPlainShort { get; set; } public IList tags { get; set; } - public IList Images { get; set; } + public Lazy Content { get; set; } } } diff --git a/src/TerribleDev.Blog.Web/Models/PostContent.cs b/src/TerribleDev.Blog.Web/Models/PostContent.cs new file mode 100644 index 0000000..cb0ce1c --- /dev/null +++ b/src/TerribleDev.Blog.Web/Models/PostContent.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Html; + +namespace TerribleDev.Blog.Web.Models +{ + + public class PostContent : IPostContent + { + public HtmlString Content { get; set; } + public HtmlString Summary { get; set; } + public string ContentPlain { get; set; } + public string SummaryPlain { get; set; } + public string SummaryPlainShort { get; set; } + public IList Images { get; set; } + } +} \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Startup.cs b/src/TerribleDev.Blog.Web/Startup.cs index 05f726e..1d29a48 100644 --- a/src/TerribleDev.Blog.Web/Startup.cs +++ b/src/TerribleDev.Blog.Web/Startup.cs @@ -39,8 +39,16 @@ namespace TerribleDev.Blog.Web services.AddSingleton(getBlog()); } services.AddSingleton((i) => { - var posts = new BlogFactory().GetAllPosts(Env.IsDevelopment() ? "https://localhost:5001": "https://blog.terrible.dev"); - return BlogCacheFactory.ProjectPostCache(posts); + var posts = new BlogFactory().GetAllPostsAsync(Env.IsDevelopment() ? "https://localhost:5001": "https://blog.terrible.dev").Result; + var postCache = BlogCacheFactory.ProjectPostCache(posts); + if(Env.IsProduction()) { + foreach(var post in postCache.PostsAsLists) + { + // if we are in production turn off lazy loading + var value = post.Content.Value; + } + } + return postCache; }); services.AddApplicationInsightsTelemetry(); var controllerBuilder = services.AddControllersWithViews(); diff --git a/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml b/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml index bab6965..7689408 100644 --- a/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Home/Post.cshtml @@ -9,26 +9,26 @@ @section Head { - + - + - + - @foreach(var image in Model.Images.Take(6)) + @foreach(var image in Model.Content.Value.Images.Take(6)) { } - @if(Model.Images.Count > 0) + @if(Model.Content.Value.Images.Count > 0) { - + } } diff --git a/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml b/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml index ce30b13..25a4256 100644 --- a/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Shared/DisplayTemplates/Post.cshtml @@ -3,7 +3,7 @@

@Model.Title

- @Model.Content + @Model.Content.Value.Content @if (Model.tags.Count > 0) {
diff --git a/src/TerribleDev.Blog.Web/Views/Shared/PostSummary.cshtml b/src/TerribleDev.Blog.Web/Views/Shared/PostSummary.cshtml index 5e9d9de..e12b9fb 100644 --- a/src/TerribleDev.Blog.Web/Views/Shared/PostSummary.cshtml +++ b/src/TerribleDev.Blog.Web/Views/Shared/PostSummary.cshtml @@ -4,7 +4,7 @@

@Model.Title

- @Model.Summary + @Model.Content.Value.Summary
Continue Reading