Merge branch 'master' of github.com:dotnet-campus/dotnetCampus.ApplicationStartupManager into t/lindexi/App
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
using System.Threading.Tasks;
|
||||
//using dotnetCampus.Configurations;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示启动任务的上下文接口
|
||||
/// </summary>
|
||||
public interface IStartupContext
|
||||
{
|
||||
//IAppConfigurator Configs { get; }
|
||||
//Task<string> ReadCacheAsync(string key, string @default = "");
|
||||
/// <summary>
|
||||
/// 等待某个启动任务完成
|
||||
/// </summary>
|
||||
/// <param name="startupKey"></param>
|
||||
/// <returns></returns>
|
||||
/// 这是框架层需要支持的能力,因此也就放在此
|
||||
Task WaitStartupTaskAsync(string startupKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,27 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动模块的日志
|
||||
/// </summary>
|
||||
public interface IStartupLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// 打点某个里程碑,将会自动与上个里程碑记录时间差
|
||||
/// </summary>
|
||||
/// <param name="milestoneName"></param>
|
||||
void RecordTime(string milestoneName);
|
||||
/// <summary>
|
||||
/// 记录某个任务的耗时情况
|
||||
/// </summary>
|
||||
/// <param name="taskName"></param>
|
||||
/// <param name="task"></param>
|
||||
/// <returns></returns>
|
||||
Task RecordDuration(string taskName, Func<Task<string>> task);
|
||||
/// <summary>
|
||||
/// 上报启动结果
|
||||
/// </summary>
|
||||
/// <param name="wrappers"></param>
|
||||
void ReportResult(IReadOnlyList<IStartupTaskWrapper> wrappers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,24 @@
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动流程管理器
|
||||
/// </summary>
|
||||
public interface IStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 等待某个启动项的完成
|
||||
/// </summary>
|
||||
/// <param name="startupTaskKey"></param>
|
||||
/// <returns></returns>
|
||||
Task WaitStartupTaskAsync(string startupTaskKey);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的启动项
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
/// 理论上相同的类型的启动项不会被重复加入,基本都是一个启动项一个类型
|
||||
StartupTaskBase GetStartupTask<T>() where T : StartupTaskBase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,26 @@
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动项在启动框架的信息
|
||||
/// </summary>
|
||||
public interface IStartupTaskWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// 跟随等待着当前启动项的其他启动项
|
||||
/// </summary>
|
||||
HashSet<string> FollowTasks { get; }
|
||||
/// <summary>
|
||||
/// 当前启动项所依赖的其他启动项
|
||||
/// </summary>
|
||||
HashSet<string> Dependencies { get; }
|
||||
/// <summary>
|
||||
/// 当前启动项的标识
|
||||
/// </summary>
|
||||
string StartupTaskKey { get; }
|
||||
/// <summary>
|
||||
/// 表示当前启动项是否只能在 UI 线程启动
|
||||
/// </summary>
|
||||
bool UIOnly { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于启动项里的值提供器,用于多个启动项之间传递参数
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IStartupValueProvider<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取启动项提供的值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
T ProvideValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 一个表示空白的启动项
|
||||
/// </summary>
|
||||
internal sealed class NullObjectStartup : StartupTaskBase
|
||||
{
|
||||
|
||||
|
||||
@@ -1,36 +1,26 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
//using dotnetCampus.Configurations;
|
||||
//using dotnetCampus.Configurations.Core;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动项的上下文信息,业务方可以自己再定义而不需要使用此类型
|
||||
/// </summary>
|
||||
internal class StartupContext : IStartupContext
|
||||
{
|
||||
public IStartupLogger Logger { get; }
|
||||
|
||||
//public FileConfigurationRepo Configuration { get; }
|
||||
|
||||
//public IAppConfigurator Configs { get; }
|
||||
|
||||
public Func<Exception, Task> FastFail { get; }
|
||||
|
||||
private readonly Func<string, Task> _waitStartupTaskAsync;
|
||||
|
||||
//public Task<string> ReadCacheAsync(string key, string @default = "")
|
||||
//{
|
||||
// return Configuration.TryReadAsync(key, @default);
|
||||
//}
|
||||
|
||||
Task IStartupContext.WaitStartupTaskAsync(string startupKey) => _waitStartupTaskAsync(startupKey);
|
||||
|
||||
public StartupContext(IStartupLogger logger, /*FileConfigurationRepo configuration,*/
|
||||
Func<Exception, Task> fastFailAction, Func<string, Task> waitStartupAsync)
|
||||
{
|
||||
Logger = logger;
|
||||
//Configuration = configuration;
|
||||
_waitStartupTaskAsync = waitStartupAsync;
|
||||
//Configs = configuration.CreateAppConfigurator();
|
||||
FastFail = fastFailAction ?? (exception => StartupTaskBase.CompletedTask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,36 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示启动任务的基础日志,推荐业务方提供更贴合业务的日志,和日志记录的方法
|
||||
/// </summary>
|
||||
public class StartupLoggerBase : IStartupLogger
|
||||
{
|
||||
private readonly Stopwatch _mainWatch;
|
||||
|
||||
/// <summary>
|
||||
/// 各个启动的里程碑的信息,包括里程碑的所运行的线程名,启动时间和执行时间
|
||||
/// <para>
|
||||
/// Key: 启动的里程碑名
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Value: 启动的信息,包括里程碑的所运行的线程名,启动时间和执行时间。时间都是从 <see cref="Stopwatch.ElapsedTicks"/> 获取
|
||||
/// </para>
|
||||
/// </summary>
|
||||
protected ConcurrentDictionary<string, (string threadName, long start, long elapsed)>
|
||||
MilestoneDictionary
|
||||
{ get; } = new ConcurrentDictionary<string, (string, long, long)>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建启动任务的基础日志,此日志的核心功能就是监控启动项的启动时间
|
||||
/// </summary>
|
||||
public StartupLoggerBase()
|
||||
{
|
||||
_mainWatch = new Stopwatch();
|
||||
_mainWatch.Start();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RecordTime(string milestoneName)
|
||||
{
|
||||
var start = MilestoneDictionary.Count > 0
|
||||
@@ -34,6 +50,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
start, end - start);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task RecordDuration(string taskName, Func<Task<string>> task)
|
||||
{
|
||||
var threadName = "null";
|
||||
@@ -51,8 +68,10 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual void ReportResult(IReadOnlyList<IStartupTaskWrapper> wrappers)
|
||||
{
|
||||
// 没有实际的可以记录的地方,需要业务方自己实现记录到哪
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,15 +9,16 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动任务管理器,这是此框架的入口
|
||||
/// <para>
|
||||
/// 期望业务方自己再基于此类型继续实现自己的业务逻辑,例如注入业务方需要的日志模块,以及构建启动任务上下文
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class StartupManagerBase : IStartupManager
|
||||
{
|
||||
private readonly IMainThreadDispatcher _dispatcher;
|
||||
|
||||
///// <summary>
|
||||
///// Builder 模式所需状态:包含当前剩余需要管理的启动任务程序集。
|
||||
///// </summary>
|
||||
//private readonly List<Assembly> _assembliesToBeManaged = new List<Assembly>();
|
||||
|
||||
/// <summary>
|
||||
/// Builder 模式所需状态:包含当前所有的关键启动任务。
|
||||
/// </summary>
|
||||
@@ -42,10 +43,25 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
private List<StartupTaskWrapper>? Graph { get; set; }
|
||||
|
||||
private StartupContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取启动任务上下文信息
|
||||
/// </summary>
|
||||
protected IStartupContext StartupContext => Context;
|
||||
|
||||
/// <summary>
|
||||
/// 获取启动任务日志
|
||||
/// </summary>
|
||||
protected IStartupLogger Logger => Context.Logger;
|
||||
|
||||
/// <summary>
|
||||
/// 创建启动任务管理器
|
||||
/// </summary>
|
||||
/// <param name="logger">启动项的日志记录器</param>
|
||||
/// <param name="fastFailAction">启动过程存在失败时需要执行的逻辑,也就是说此委托被框架执行时,将应该记录失败然后退出应用</param>
|
||||
/// <param name="dispatcher">传入线程调度器,需要业务方自己实现</param>
|
||||
/// <param name="shouldSetThreadPool">是否需要在进入启动框架时,让框架设置线程池的线程数</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public StartupManagerBase(IStartupLogger logger, /*FileConfigurationRepo configurationRepo,*/
|
||||
Func<Exception, Task> fastFailAction, IMainThreadDispatcher dispatcher, bool shouldSetThreadPool = true)
|
||||
{
|
||||
@@ -213,12 +229,20 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
// return this;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 加入构建预设启动项
|
||||
/// </summary>
|
||||
/// <param name="taskBuilder"></param>
|
||||
/// <returns></returns>
|
||||
public StartupManagerBase ForStartupTasksOfCategory(Action<StartupTaskBuilder> taskBuilder)
|
||||
{
|
||||
_additionalBuilders.Add(taskBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动框架,开始执行应用程序的启动流程
|
||||
/// </summary>
|
||||
public async void Run()
|
||||
{
|
||||
if (Graph == null)
|
||||
@@ -352,6 +376,10 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出所有的启动任务项,可以给子类继承,用于让子类定制获取启动任务项的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<StartupTaskMetadata> ExportStartupTasks()
|
||||
{
|
||||
foreach (var func in _startupTaskMetadataCollectorList)
|
||||
@@ -444,6 +472,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
throw new InvalidOperationException($"{startupTaskKey}既无法添加至字典,也无法从字典获取对应值");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task WaitStartupTaskAsync(string startupTaskKey)
|
||||
=> GetStartupTaskWrapper(startupTaskKey).TaskBase.TaskResult;
|
||||
|
||||
@@ -453,6 +482,13 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
private static string StartupTypeToKey(Type type)
|
||||
=> type.Name.Remove(type.Name.Length - "startup".Length);
|
||||
|
||||
/// <summary>
|
||||
/// 实际执行某个具体的启动任务项的函数,可以给子类继承,用来控制具体的启动任务项执行逻辑
|
||||
/// </summary>
|
||||
/// <param name="startupTask"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="uiOnly"></param>
|
||||
/// <returns></returns>
|
||||
protected internal virtual Task<string> ExecuteStartupTaskAsync(StartupTaskBase startupTask, IStartupContext context, bool uiOnly)
|
||||
{
|
||||
return startupTask.JoinAsync(context, uiOnly);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动任务的特性,可以让业务方用来对接,如对接预编译框架,从而收集启动任务项
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class StartupTaskAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -4,10 +4,22 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动任务项的基类型
|
||||
/// </summary>
|
||||
public abstract class StartupTaskBase
|
||||
{
|
||||
// 由于我们都在编译期间收集 Attribute 了,当然也能收集使用方到底重写了哪个 Run。
|
||||
// 这里传入的 isUIOnly 就是编译期间收集的那个属性。
|
||||
/// <summary>
|
||||
/// 将当前启动任务项加入执行
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 此函数由启动框架调用
|
||||
/// </remarks>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="isUIOnly"></param>
|
||||
/// <returns></returns>
|
||||
/// 由于我们都在编译期间收集 Attribute 了,当然也能收集使用方到底重写了哪个 Run。
|
||||
/// 这里传入的 isUIOnly 就是编译期间收集的那个属性。
|
||||
public async Task<string> JoinAsync(IStartupContext context, bool isUIOnly)
|
||||
{
|
||||
// 决定执行 Run 还是 RunAsync。
|
||||
@@ -44,11 +56,19 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动任务项的实际执行逻辑,由子类继承,实现启动任务项业务逻辑
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual Task RunAsync(IStartupContext context)
|
||||
{
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一个表示执行完成的任务,可以在 <see cref="RunAsync(IStartupContext)"/> 作为返回值
|
||||
/// </summary>
|
||||
protected internal static Task CompletedTask =>
|
||||
#if NETFRAMEWORK
|
||||
CompletedCommonTask;
|
||||
@@ -59,6 +79,9 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
Task.CompletedTask;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前启动任务项可等待任务
|
||||
/// </summary>
|
||||
public Task TaskResult => CompletedSource.Task;
|
||||
|
||||
private TaskCompletionSource<object?> CompletedSource { get; } = new TaskCompletionSource<object?>();
|
||||
@@ -67,6 +90,12 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
// 框架注入,一定不为空
|
||||
= null!;
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个指定传入的启动任务项的提供的参数
|
||||
/// </summary>
|
||||
/// <typeparam name="TStartup">提供参数的启动任务项</typeparam>
|
||||
/// <typeparam name="TValue">参数</typeparam>
|
||||
/// <returns></returns>
|
||||
protected TValue FetchValue<TStartup, TValue>() where TStartup : StartupTaskBase, IStartupValueProvider<TValue>
|
||||
{
|
||||
var task = Manager.GetStartupTask<TStartup>();
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace dotnetCampus.ApplicationStartupManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 启动流程任务创建器
|
||||
/// </summary>
|
||||
public class StartupTaskBuilder
|
||||
{
|
||||
private readonly StartupTaskWrapper _wrapper;
|
||||
@@ -17,18 +20,31 @@ namespace dotnetCampus.ApplicationStartupManager
|
||||
_addFollowTasksAction = addFollowTasksAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置或获取启动流程的分类
|
||||
/// </summary>
|
||||
public StartupCategory Categories
|
||||
{
|
||||
get => _wrapper.Categories;
|
||||
set => _wrapper.Categories = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加上依赖的启动任务项
|
||||
/// </summary>
|
||||
/// <param name="afterTasks"></param>
|
||||
/// <returns></returns>
|
||||
public StartupTaskBuilder AddDependencies(string afterTasks)
|
||||
{
|
||||
_addDependenciesAction(_wrapper, afterTasks);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加上跟随当前启动任务项的启动任务项
|
||||
/// </summary>
|
||||
/// <param name="beforeTasks"></param>
|
||||
/// <returns></returns>
|
||||
public StartupTaskBuilder AddFollowTasks(string beforeTasks)
|
||||
{
|
||||
_addFollowTasksAction(_wrapper, beforeTasks);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.0;net45;net5.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp3.1;netstandard2.0;net45;net5.0;net6.0</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
|
||||
<!-- <GenerateDocumentationFile>true</GenerateDocumentationFile> -->
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user