From 2c6414dbeadd337c57caac723c6415e47826a195 Mon Sep 17 00:00:00 2001 From: jackfiled Date: Tue, 23 Jan 2024 20:37:41 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E6=B8=B2=E6=9F=93=E5=89=8D=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=99=A8=E5=92=8C=E6=B8=B2=E6=9F=93=E5=90=8E=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Abstractions/IPostRenderProcessor.cs | 10 +++ .../Abstractions/IPreRenderProcessor.cs | 10 +++ .../BlogApplicationBuilderExtension.cs | 47 +++++++++++ .../Extensions/BlogApplicationExtension.cs | 50 ++++------- YaeBlog.Core/Services/RendererService.cs | 84 +++++++++++++++++-- 5 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 YaeBlog.Core/Abstractions/IPostRenderProcessor.cs create mode 100644 YaeBlog.Core/Abstractions/IPreRenderProcessor.cs create mode 100644 YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs diff --git a/YaeBlog.Core/Abstractions/IPostRenderProcessor.cs b/YaeBlog.Core/Abstractions/IPostRenderProcessor.cs new file mode 100644 index 0000000..3af577c --- /dev/null +++ b/YaeBlog.Core/Abstractions/IPostRenderProcessor.cs @@ -0,0 +1,10 @@ +using YaeBlog.Core.Models; + +namespace YaeBlog.Core.Abstractions; + +public interface IPostRenderProcessor +{ + BlogEssay Process(BlogEssay essay); + + string Name { get; } +} diff --git a/YaeBlog.Core/Abstractions/IPreRenderProcessor.cs b/YaeBlog.Core/Abstractions/IPreRenderProcessor.cs new file mode 100644 index 0000000..9762e53 --- /dev/null +++ b/YaeBlog.Core/Abstractions/IPreRenderProcessor.cs @@ -0,0 +1,10 @@ +using YaeBlog.Core.Models; + +namespace YaeBlog.Core.Abstractions; + +public interface IPreRenderProcessor +{ + BlogContent Process(BlogContent content); + + string Name { get; } +} diff --git a/YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs b/YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs new file mode 100644 index 0000000..e17927c --- /dev/null +++ b/YaeBlog.Core/Extensions/BlogApplicationBuilderExtension.cs @@ -0,0 +1,47 @@ +using Markdig; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using YaeBlog.Core.Builder; +using YaeBlog.Core.Models; +using YaeBlog.Core.Services; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace YaeBlog.Core.Extensions; + +public static class BlogApplicationBuilderExtension +{ + internal static void ConfigureBlogApplication(this BlogApplicationBuilder builder) + { + builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); + builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", + optional: true, reloadOnChange: true); + builder.Configuration.AddEnvironmentVariables(); + + builder.Services.Configure( + builder.Configuration.GetSection(BlogOptions.OptionName)); + + builder.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance); + builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties(); + + builder.Services.AddSingleton( + _ => builder.MarkdigPipelineBuilder.Build()); + builder.Services.AddSingleton( + _ => builder.YamlDeserializerBuilder.Build()); + + builder.Services.AddHostedService(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + } + + public static void ConfigureWebApplication(this BlogApplicationBuilder builder, + Action configureWebApplicationBuilder, + Action configureWebApplication) + { + builder.Services.AddHostedService(provider => + new WebApplicationHostedService(configureWebApplicationBuilder, + configureWebApplication, provider)); + } +} diff --git a/YaeBlog.Core/Extensions/BlogApplicationExtension.cs b/YaeBlog.Core/Extensions/BlogApplicationExtension.cs index 38eff99..a7de84b 100644 --- a/YaeBlog.Core/Extensions/BlogApplicationExtension.cs +++ b/YaeBlog.Core/Extensions/BlogApplicationExtension.cs @@ -1,47 +1,29 @@ -using Markdig; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using YaeBlog.Core.Abstractions; using YaeBlog.Core.Builder; -using YaeBlog.Core.Models; using YaeBlog.Core.Services; -using YamlDotNet.Serialization; -using YamlDotNet.Serialization.NamingConventions; namespace YaeBlog.Core.Extensions; public static class BlogApplicationExtension { - internal static void ConfigureBlogApplication(this BlogApplicationBuilder builder) + public static void UsePreRenderProcessor(this BlogApplication application) + where T : IPreRenderProcessor { - builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); - builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", - optional: true, reloadOnChange: true); - builder.Configuration.AddEnvironmentVariables(); - - builder.Services.Configure( - builder.Configuration.GetSection(BlogOptions.OptionName)); - - builder.YamlDeserializerBuilder.WithNamingConvention(CamelCaseNamingConvention.Instance); - builder.YamlDeserializerBuilder.IgnoreUnmatchedProperties(); - - builder.Services.AddSingleton( - _ => builder.MarkdigPipelineBuilder.Build()); - builder.Services.AddSingleton( - _ => builder.YamlDeserializerBuilder.Build()); - - builder.Services.AddHostedService(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); + RendererService rendererService = + application.Services.GetRequiredService(); + T preRenderProcessor = + application.Services.GetRequiredService(); + rendererService.AddPreRenderProcessor(preRenderProcessor); } - public static void ConfigureWebApplication(this BlogApplicationBuilder builder, - Action configureWebApplicationBuilder, - Action configureWebApplication) + public static void UsePostRenderProcessor(this BlogApplication application) + where T : IPostRenderProcessor { - builder.Services.AddHostedService(provider => - new WebApplicationHostedService(configureWebApplicationBuilder, - configureWebApplication, provider)); + RendererService rendererService = + application.Services.GetRequiredService(); + T postRenderProcessor = + application.Services.GetRequiredService(); + rendererService.AddPostRenderProcessor(postRenderProcessor); } } diff --git a/YaeBlog.Core/Services/RendererService.cs b/YaeBlog.Core/Services/RendererService.cs index e842ebb..ff23b38 100644 --- a/YaeBlog.Core/Services/RendererService.cs +++ b/YaeBlog.Core/Services/RendererService.cs @@ -1,6 +1,8 @@ -using System.Diagnostics; +using System.Collections.Concurrent; +using System.Diagnostics; using Markdig; using Microsoft.Extensions.Logging; +using YaeBlog.Core.Abstractions; using YaeBlog.Core.Exceptions; using YaeBlog.Core.Models; using YamlDotNet.Core; @@ -16,18 +18,23 @@ public class RendererService(ILogger logger, { private readonly Stopwatch _stopwatch = new(); + private readonly List _preRenderProcessors = []; + + private readonly List _postRenderProcessors = []; + public async Task RenderAsync() { _stopwatch.Start(); logger.LogInformation("Render essays start."); List contents = await essayScanService.ScanAsync(); + IEnumerable preProcessedContents = + PreProcess(contents); List essays = []; - await Task.Run(() => { - foreach (BlogContent content in contents) + foreach (BlogContent content in preProcessedContents) { MarkdownMetadata? metadata = TryParseMetadata(content); BlogEssay essay = new() @@ -46,6 +53,7 @@ public class RendererService(ILogger logger, } }); + ConcurrentBag postProcessEssays = []; Parallel.ForEach(essays, essay => { @@ -58,19 +66,79 @@ public class RendererService(ILogger logger, }; newEssay.Tags.AddRange(essay.Tags); - if (!essayContentService.TryAdd(newEssay.FileName, newEssay)) - { - throw new BlogFileException( - $"There are two essays with the same name: '{newEssay.FileName}'."); - } + postProcessEssays.Add(newEssay); logger.LogDebug("Render markdown file {}.", newEssay); }); + PostProcess(postProcessEssays); + _stopwatch.Stop(); logger.LogInformation("Render finished, consuming {} s.", _stopwatch.Elapsed.ToString("s\\.fff")); } + public void AddPreRenderProcessor(IPreRenderProcessor processor) + { + bool exist = _preRenderProcessors.Any(p => p.Name == processor.Name); + + if (exist) + { + throw new InvalidOperationException("There exists one pre-render processor " + + $"with the same name: {processor.Name}."); + } + + _preRenderProcessors.Add(processor); + } + + public void AddPostRenderProcessor(IPostRenderProcessor processor) + { + bool exist = _postRenderProcessors.Any(p => p.Name == processor.Name); + + if (exist) + { + throw new InvalidCastException("There exists one post-render processor " + + $"with the same name: {processor.Name}."); + } + + _postRenderProcessors.Add(processor); + } + + private IEnumerable PreProcess(IEnumerable contents) + { + ConcurrentBag processedContents = []; + + Parallel.ForEach(contents, content => + { + foreach (IPreRenderProcessor processor in _preRenderProcessors) + { + content = processor.Process(content); + } + + processedContents.Add(content); + }); + + return processedContents; + } + + private void PostProcess(IEnumerable essays) + { + Parallel.ForEach(essays, essay => + { + foreach (IPostRenderProcessor processor in _postRenderProcessors) + { + essay = processor.Process(essay); + } + + if (!essayContentService.TryAdd(essay.FileName, essay)) + { + throw new BlogFileException( + $"There are two essays with the same name: '{essay.FileName}'."); + } + + logger.LogDebug("Post-Process essay: {}.", essay); + }); + } + private MarkdownMetadata? TryParseMetadata(BlogContent content) { string fileContent = content.FileContent.Trim();