Merge branch 'master' of github.com:dotnet-campus/dotnetCampus.ApplicationStartupManager into t/lindexi/ci
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using dotnetCampus.Configurations;
|
//using dotnetCampus.Configurations;
|
||||||
|
|
||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
public interface IStartupContext
|
public interface IStartupContext
|
||||||
{
|
{
|
||||||
IAppConfigurator Configs { get; }
|
//IAppConfigurator Configs { get; }
|
||||||
Task<string> ReadCacheAsync(string key, string @default = "");
|
//Task<string> ReadCacheAsync(string key, string @default = "");
|
||||||
Task WaitStartupTaskAsync(string startupKey);
|
Task WaitStartupTaskAsync(string startupKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,6 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
{
|
{
|
||||||
Task WaitStartupTaskAsync(string startupTaskKey);
|
Task WaitStartupTaskAsync(string startupTaskKey);
|
||||||
|
|
||||||
StartupTask GetStartupTask<T>() where T : StartupTask;
|
StartupTaskBase GetStartupTask<T>() where T : StartupTaskBase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
internal sealed class NullObjectStartup : StartupTask
|
internal sealed class NullObjectStartup : StartupTaskBase
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using dotnetCampus.Configurations;
|
//using dotnetCampus.Configurations;
|
||||||
using dotnetCampus.Configurations.Core;
|
//using dotnetCampus.Configurations.Core;
|
||||||
|
|
||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
@@ -9,29 +9,29 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
{
|
{
|
||||||
public IStartupLogger Logger { get; }
|
public IStartupLogger Logger { get; }
|
||||||
|
|
||||||
public FileConfigurationRepo Configuration { get; }
|
//public FileConfigurationRepo Configuration { get; }
|
||||||
|
|
||||||
public IAppConfigurator Configs { get; }
|
//public IAppConfigurator Configs { get; }
|
||||||
|
|
||||||
public Func<Exception, Task> FastFail { get; }
|
public Func<Exception, Task> FastFail { get; }
|
||||||
|
|
||||||
private readonly Func<string, Task> _waitStartupTaskAsync;
|
private readonly Func<string, Task> _waitStartupTaskAsync;
|
||||||
|
|
||||||
public Task<string> ReadCacheAsync(string key, string @default = "")
|
//public Task<string> ReadCacheAsync(string key, string @default = "")
|
||||||
{
|
//{
|
||||||
return Configuration.TryReadAsync(key, @default);
|
// return Configuration.TryReadAsync(key, @default);
|
||||||
}
|
//}
|
||||||
|
|
||||||
Task IStartupContext.WaitStartupTaskAsync(string startupKey) => _waitStartupTaskAsync(startupKey);
|
Task IStartupContext.WaitStartupTaskAsync(string startupKey) => _waitStartupTaskAsync(startupKey);
|
||||||
|
|
||||||
public StartupContext(IStartupLogger logger, FileConfigurationRepo configuration,
|
public StartupContext(IStartupLogger logger, /*FileConfigurationRepo configuration,*/
|
||||||
Func<Exception, Task> fastFailAction, Func<string, Task> waitStartupAsync)
|
Func<Exception, Task> fastFailAction, Func<string, Task> waitStartupAsync)
|
||||||
{
|
{
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
Configuration = configuration;
|
//Configuration = configuration;
|
||||||
_waitStartupTaskAsync = waitStartupAsync;
|
_waitStartupTaskAsync = waitStartupAsync;
|
||||||
Configs = configuration.CreateAppConfigurator();
|
//Configs = configuration.CreateAppConfigurator();
|
||||||
FastFail = fastFailAction ?? (exception => StartupTask.CompletedTask);
|
FastFail = fastFailAction ?? (exception => StartupTaskBase.CompletedTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,14 +9,14 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
public class StartupLogger : IStartupLogger
|
public class StartupLoggerBase : IStartupLogger
|
||||||
{
|
{
|
||||||
private readonly Stopwatch _mainWatch;
|
private readonly Stopwatch _mainWatch;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, (string name, long start, long elapsed)>
|
protected ConcurrentDictionary<string, (string threadName, long start, long elapsed)>
|
||||||
_milestoneDictionary = new ConcurrentDictionary<string, (string, long, long)>();
|
MilestoneDictionary { get; } = new ConcurrentDictionary<string, (string, long, long)>();
|
||||||
|
|
||||||
public StartupLogger()
|
public StartupLoggerBase()
|
||||||
{
|
{
|
||||||
_mainWatch = new Stopwatch();
|
_mainWatch = new Stopwatch();
|
||||||
_mainWatch.Start();
|
_mainWatch.Start();
|
||||||
@@ -24,11 +24,11 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
|
|
||||||
public void RecordTime(string milestoneName)
|
public void RecordTime(string milestoneName)
|
||||||
{
|
{
|
||||||
var start = _milestoneDictionary.Count > 0
|
var start = MilestoneDictionary.Count > 0
|
||||||
? _milestoneDictionary.Max(x => x.Value.start + x.Value.elapsed)
|
? MilestoneDictionary.Max(x => x.Value.start + x.Value.elapsed)
|
||||||
: 0;
|
: 0;
|
||||||
var end = _mainWatch.ElapsedTicks;
|
var end = _mainWatch.ElapsedTicks;
|
||||||
_milestoneDictionary[milestoneName] =
|
MilestoneDictionary[milestoneName] =
|
||||||
(Thread.CurrentThread.Name ?? Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture),
|
(Thread.CurrentThread.Name ?? Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture),
|
||||||
start, end - start);
|
start, end - start);
|
||||||
}
|
}
|
||||||
@@ -46,13 +46,12 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
{
|
{
|
||||||
var end = _mainWatch.ElapsedTicks;
|
var end = _mainWatch.ElapsedTicks;
|
||||||
var elapse = end - begin;
|
var elapse = end - begin;
|
||||||
_milestoneDictionary[taskName] = (threadName, begin, elapse);
|
MilestoneDictionary[taskName] = (threadName, begin, elapse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReportResult(IReadOnlyList<IStartupTaskWrapper> wrappers)
|
public virtual void ReportResult(IReadOnlyList<IStartupTaskWrapper> wrappers)
|
||||||
{
|
{
|
||||||
// todo 还没有具体实现
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,24 +2,21 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Mime;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using dotnetCampus.Configurations.Core;
|
//using dotnetCampus.Configurations.Core;
|
||||||
|
|
||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
public class StartupManager : IStartupManager
|
public class StartupManagerBase : IStartupManager
|
||||||
{
|
{
|
||||||
private readonly IMainThreadDispatcher _dispatcher;
|
private readonly IMainThreadDispatcher _dispatcher;
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// Builder 模式所需状态:包含当前剩余需要管理的启动任务程序集。
|
///// Builder 模式所需状态:包含当前剩余需要管理的启动任务程序集。
|
||||||
/// </summary>
|
///// </summary>
|
||||||
private readonly List<Assembly> _assembliesToBeManaged = new List<Assembly>();
|
//private readonly List<Assembly> _assembliesToBeManaged = new List<Assembly>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builder 模式所需状态:包含当前所有的关键启动任务。
|
/// Builder 模式所需状态:包含当前所有的关键启动任务。
|
||||||
@@ -42,13 +39,14 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
internal ConcurrentDictionary<string, StartupTaskWrapper> StartupTaskWrappers { get; } =
|
internal ConcurrentDictionary<string, StartupTaskWrapper> StartupTaskWrappers { get; } =
|
||||||
new ConcurrentDictionary<string, StartupTaskWrapper>();
|
new ConcurrentDictionary<string, StartupTaskWrapper>();
|
||||||
|
|
||||||
private List<StartupTaskWrapper> Graph { get; set; }
|
private List<StartupTaskWrapper>? Graph { get; set; }
|
||||||
|
|
||||||
private StartupContext Context { get; }
|
private StartupContext Context { get; }
|
||||||
|
protected IStartupContext StartupContext => Context;
|
||||||
|
|
||||||
private IStartupLogger Logger => Context.Logger;
|
protected IStartupLogger Logger => Context.Logger;
|
||||||
|
|
||||||
public StartupManager(IStartupLogger logger, FileConfigurationRepo configurationRepo,
|
public StartupManagerBase(IStartupLogger logger, /*FileConfigurationRepo configurationRepo,*/
|
||||||
Func<Exception, Task> fastFailAction, IMainThreadDispatcher dispatcher, bool shouldSetThreadPool = true)
|
Func<Exception, Task> fastFailAction, IMainThreadDispatcher dispatcher, bool shouldSetThreadPool = true)
|
||||||
{
|
{
|
||||||
if (logger == null)
|
if (logger == null)
|
||||||
@@ -56,10 +54,10 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
throw new ArgumentNullException(nameof(logger));
|
throw new ArgumentNullException(nameof(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configurationRepo is null)
|
//if (configurationRepo is null)
|
||||||
{
|
//{
|
||||||
throw new ArgumentNullException(nameof(configurationRepo));
|
// throw new ArgumentNullException(nameof(configurationRepo));
|
||||||
}
|
//}
|
||||||
|
|
||||||
_dispatcher = dispatcher;
|
_dispatcher = dispatcher;
|
||||||
|
|
||||||
@@ -71,40 +69,56 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
ThreadPool.SetMinThreads(Math.Max(_workerThreads, 16), Math.Max(_completionPortThreads, 16));
|
ThreadPool.SetMinThreads(Math.Max(_workerThreads, 16), Math.Max(_completionPortThreads, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
Context = new StartupContext(logger, configurationRepo,
|
Context = new StartupContext(logger, /*configurationRepo,*/
|
||||||
fastFailAction, WaitStartupTaskAsync);
|
fastFailAction, WaitStartupTaskAsync);
|
||||||
|
|
||||||
Logger.RecordTime("ManagerInitialized");
|
Logger.RecordTime("ManagerInitialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 配置被 <see cref="StartupManager"/> 管理的程序集。
|
||||||
|
///// 只有被管理的程序集中的启动信息、依赖注入信息才会被执行。
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="assemblies"></param>
|
||||||
|
///// <returns></returns>
|
||||||
|
//public StartupManagerBase ConfigAssemblies(IEnumerable<Assembly> assemblies)
|
||||||
|
//{
|
||||||
|
// // 可能的限制尚未完成:
|
||||||
|
// // 1. Run 之后不能再调用此方法(适用于固定的程序集应用);
|
||||||
|
// // 2. 可以多次 Config 然后多次 Run(适用于动态加载的插件程序集)。
|
||||||
|
// _assembliesToBeManaged.AddRange(assemblies);
|
||||||
|
// return this;
|
||||||
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置被 <see cref="StartupManager"/> 管理的程序集。
|
/// 配置被 <see cref="StartupManagerBase"/> 管理的程序集。
|
||||||
/// 只有被管理的程序集中的启动信息、依赖注入信息才会被执行。
|
/// 只有被管理的程序集中的启动信息、依赖注入信息才会被执行。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="assemblies"></param>
|
/// <param name="collector"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public StartupManager ConfigAssemblies(IEnumerable<Assembly> assemblies)
|
public virtual StartupManagerBase AddStartupTaskMetadataCollector(Func<IEnumerable<StartupTaskMetadata>> collector)
|
||||||
{
|
{
|
||||||
// 可能的限制尚未完成:
|
// 可能的限制尚未完成:
|
||||||
// 1. Run 之后不能再调用此方法(适用于固定的程序集应用);
|
// 1. Run 之后不能再调用此方法(适用于固定的程序集应用);
|
||||||
// 2. 可以多次 Config 然后多次 Run(适用于动态加载的插件程序集)。
|
// 2. 可以多次 Config 然后多次 Run(适用于动态加载的插件程序集)。
|
||||||
_assembliesToBeManaged.AddRange(assemblies);
|
_startupTaskMetadataCollectorList.Add(collector);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly ConcurrentBag<Func<IEnumerable<StartupTaskMetadata>>> _startupTaskMetadataCollectorList = new ConcurrentBag<Func<IEnumerable<StartupTaskMetadata>>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在启动流程中定义一组关键的启动节点。使用此方法定义的关键启动节点将按顺序前后依次依赖。
|
/// 在启动流程中定义一组关键的启动节点。使用此方法定义的关键启动节点将按顺序前后依次依赖。
|
||||||
/// 例如传入 A、B、C、D 四个关键启动节点,那么 A - B - C - D 将依次执行,其他任务将插入其中。
|
/// 例如传入 A、B、C、D 四个关键启动节点,那么 A - B - C - D 将依次执行,其他任务将插入其中。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="criticalNodeKeys">关键启动节点的名称。</param>
|
/// <param name="criticalNodeKeys">关键启动节点的名称。</param>
|
||||||
/// <returns><see cref="StartupManager"/> 实例自身,用于使用重建者模式创建启动流程管理器。</returns>
|
/// <returns><see cref="StartupManagerBase"/> 实例自身,用于使用重建者模式创建启动流程管理器。</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 通常情况下你可以编写常规的 StartupTask 来添加一个关键节点,只要这个节点被众多节点声明了依赖,那么它就能视为一个关键节点。
|
/// 通常情况下你可以编写常规的 StartupTask 来添加一个关键节点,只要这个节点被众多节点声明了依赖,那么它就能视为一个关键节点。
|
||||||
/// 使用此方法可以添加一些虚拟的,实际上不会执行任何启动任务的关键启动节点,用于汇总其他模块的依赖关系。
|
/// 使用此方法可以添加一些虚拟的,实际上不会执行任何启动任务的关键启动节点,用于汇总其他模块的依赖关系。
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public StartupManager UseCriticalNodes(params string[] criticalNodeKeys)
|
public StartupManagerBase UseCriticalNodes(params string[] criticalNodeKeys)
|
||||||
{
|
{
|
||||||
if (criticalNodeKeys == null)
|
if (criticalNodeKeys == null)
|
||||||
{
|
{
|
||||||
@@ -129,7 +143,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
{
|
{
|
||||||
var key = criticalNodeKeys[i];
|
var key = criticalNodeKeys[i];
|
||||||
var current = GetStartupTaskWrapper(key);
|
var current = GetStartupTaskWrapper(key);
|
||||||
current.StartupTask = new NullObjectStartup();
|
current.TaskBase = new NullObjectStartup();
|
||||||
current.Categories = StartupCategory.All;
|
current.Categories = StartupCategory.All;
|
||||||
|
|
||||||
if (i - 1 >= 0)
|
if (i - 1 >= 0)
|
||||||
@@ -153,7 +167,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
/// <param name="nodeName">关键节点的名称。</param>
|
/// <param name="nodeName">关键节点的名称。</param>
|
||||||
/// <param name="beforeTasks">关键节点的前置节点。</param>
|
/// <param name="beforeTasks">关键节点的前置节点。</param>
|
||||||
/// <param name="afterTasks">关键节点的后置节点。</param>
|
/// <param name="afterTasks">关键节点的后置节点。</param>
|
||||||
/// <returns><see cref="StartupManager"/> 实例自身,用于使用重建者模式创建启动流程管理器。</returns>
|
/// <returns><see cref="StartupManagerBase"/> 实例自身,用于使用重建者模式创建启动流程管理器。</returns>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// 通常情况下你可以编写常规的 StartupTask 来添加一个关键节点,只要这个节点被众多节点声明了依赖,那么它就能视为一个关键节点。
|
/// 通常情况下你可以编写常规的 StartupTask 来添加一个关键节点,只要这个节点被众多节点声明了依赖,那么它就能视为一个关键节点。
|
||||||
@@ -164,13 +178,13 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
/// 例如,你需要根据不同的启动条件决定不同的启动顺序,那么你可能需要使用此方法动态生成关键节点。
|
/// 例如,你需要根据不同的启动条件决定不同的启动顺序,那么你可能需要使用此方法动态生成关键节点。
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public StartupManager AddCriticalNodes(string nodeName, string beforeTasks = null, string afterTasks = null)
|
public StartupManagerBase AddCriticalNodes(string nodeName, string? beforeTasks = null, string? afterTasks = null)
|
||||||
{
|
{
|
||||||
var wrapper = GetStartupTaskWrapper(nodeName);
|
var wrapper = GetStartupTaskWrapper(nodeName);
|
||||||
wrapper.StartupTask = new NullObjectStartup();
|
wrapper.TaskBase = new NullObjectStartup();
|
||||||
wrapper.Categories = StartupCategory.All;
|
wrapper.Categories = StartupCategory.All;
|
||||||
|
|
||||||
if (beforeTasks?.Split(new[] {";"}, StringSplitOptions.RemoveEmptyEntries) is string[] before)
|
if (beforeTasks?.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries) is { } before)
|
||||||
{
|
{
|
||||||
foreach (var b in before)
|
foreach (var b in before)
|
||||||
{
|
{
|
||||||
@@ -179,7 +193,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (afterTasks?.Split(new[] {";"}, StringSplitOptions.RemoveEmptyEntries) is string[] after)
|
if (afterTasks?.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries) is { } after)
|
||||||
{
|
{
|
||||||
foreach (var a in after)
|
foreach (var a in after)
|
||||||
{
|
{
|
||||||
@@ -192,22 +206,16 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StartupManager SelectNodes(StartupCategory categories)
|
// 不支持被使用
|
||||||
{
|
//public StartupManagerBase SelectNodes(StartupCategory categories)
|
||||||
_selectingCategories = categories;
|
//{
|
||||||
return this;
|
// _selectingCategories = categories;
|
||||||
}
|
// return this;
|
||||||
|
//}
|
||||||
|
|
||||||
public StartupManager ForStartupTasksOfCategory(StartupCategory category,
|
public StartupManagerBase ForStartupTasksOfCategory(Action<StartupTaskBuilder> taskBuilder)
|
||||||
Action<StartupTaskBuilder> taskBuilder)
|
|
||||||
{
|
{
|
||||||
_additionalBuilders.Add(builder =>
|
_additionalBuilders.Add(taskBuilder);
|
||||||
{
|
|
||||||
if (builder.Categories == category)
|
|
||||||
{
|
|
||||||
taskBuilder(builder);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +230,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
var dispatcher = _dispatcher;
|
var dispatcher = _dispatcher;
|
||||||
foreach (var wrapper in Graph)
|
foreach (var wrapper in Graph)
|
||||||
{
|
{
|
||||||
var startupTasks = wrapper.Dependencies.Select(s => GetStartupTaskWrapper(s).StartupTask);
|
var startupTasks = wrapper.Dependencies.Select(s => GetStartupTaskWrapper(s).TaskBase);
|
||||||
if (wrapper.UIOnly)
|
if (wrapper.UIOnly)
|
||||||
{
|
{
|
||||||
await dispatcher.InvokeAsync(() => wrapper.ExecuteTask(startupTasks, Context));
|
await dispatcher.InvokeAsync(() => wrapper.ExecuteTask(startupTasks, Context));
|
||||||
@@ -233,7 +241,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Graph.Last().StartupTask.TaskResult;
|
await Graph.Last().TaskBase.TaskResult;
|
||||||
|
|
||||||
Logger.RecordTime("AllStartupTasksCompleted");
|
Logger.RecordTime("AllStartupTasksCompleted");
|
||||||
|
|
||||||
@@ -254,7 +262,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
wrapper.Categories &= _selectingCategories;
|
wrapper.Categories &= _selectingCategories;
|
||||||
}
|
}
|
||||||
|
|
||||||
var taskMetadataList = ExportStartupTasks(_assembliesToBeManaged, _selectingCategories).ToList();
|
var taskMetadataList = ExportStartupTasks().ToList();
|
||||||
|
|
||||||
foreach (var meta in taskMetadataList)
|
foreach (var meta in taskMetadataList)
|
||||||
{
|
{
|
||||||
@@ -262,8 +270,8 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
wrapper.UIOnly = meta.Scheduler == StartupScheduler.UIOnly;
|
wrapper.UIOnly = meta.Scheduler == StartupScheduler.UIOnly;
|
||||||
wrapper.Categories = meta.Categories;
|
wrapper.Categories = meta.Categories;
|
||||||
wrapper.CriticalLevel = meta.CriticalLevel;
|
wrapper.CriticalLevel = meta.CriticalLevel;
|
||||||
wrapper.StartupTask = meta.Instance;
|
wrapper.TaskBase = meta.Instance;
|
||||||
wrapper.StartupTask.Manager = this;
|
wrapper.TaskBase.Manager = this;
|
||||||
wrappers.Add(wrapper);
|
wrappers.Add(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,25 +302,9 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
|
|
||||||
return DFSGraph(wrappers);
|
return DFSGraph(wrappers);
|
||||||
|
|
||||||
IEnumerable<StartupTaskMetadata> ExportStartupTasks(
|
|
||||||
IEnumerable<Assembly> assemblies, StartupCategory categories)
|
|
||||||
{
|
|
||||||
// todo 高性能的预编译框架接入
|
|
||||||
//foreach (var meta in AssemblyMetadataExporter.ExportStartupTasks(assemblies))
|
|
||||||
//{
|
|
||||||
// meta.Categories &= categories;
|
|
||||||
// if (meta.Categories != StartupCategory.None)
|
|
||||||
// {
|
|
||||||
// yield return meta;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddDependencies(StartupTaskWrapper wrapper, string afterTasks)
|
void AddDependencies(StartupTaskWrapper wrapper, string afterTasks)
|
||||||
{
|
{
|
||||||
foreach (var task in afterTasks.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)
|
foreach (var task in afterTasks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(CompatibleTaskName))
|
.Select(CompatibleTaskName))
|
||||||
{
|
{
|
||||||
if (!taskMetadataList.Exists(metadata => metadata.Key == task)
|
if (!taskMetadataList.Exists(metadata => metadata.Key == task)
|
||||||
@@ -332,7 +324,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
|
|
||||||
void AddFollowTasks(StartupTaskWrapper wrapper, string beforeTasks)
|
void AddFollowTasks(StartupTaskWrapper wrapper, string beforeTasks)
|
||||||
{
|
{
|
||||||
foreach (var task in beforeTasks.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)
|
foreach (var task in beforeTasks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(CompatibleTaskName))
|
.Select(CompatibleTaskName))
|
||||||
{
|
{
|
||||||
if (!taskMetadataList.Exists(metadata => metadata.Key == task)
|
if (!taskMetadataList.Exists(metadata => metadata.Key == task)
|
||||||
@@ -351,6 +343,18 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual IEnumerable<StartupTaskMetadata> ExportStartupTasks()
|
||||||
|
{
|
||||||
|
foreach (var func in _startupTaskMetadataCollectorList)
|
||||||
|
{
|
||||||
|
var taskMetadataList = func();
|
||||||
|
foreach (var taskMetadata in taskMetadataList)
|
||||||
|
{
|
||||||
|
yield return taskMetadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string CompatibleTaskName(string task)
|
private static string CompatibleTaskName(string task)
|
||||||
{
|
{
|
||||||
const string startupName = "Startup";
|
const string startupName = "Startup";
|
||||||
@@ -417,7 +421,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper = new StartupTaskWrapper(startupTaskKey);
|
wrapper = new StartupTaskWrapper(startupTaskKey, this);
|
||||||
if (StartupTaskWrappers.TryAdd(startupTaskKey, wrapper))
|
if (StartupTaskWrappers.TryAdd(startupTaskKey, wrapper))
|
||||||
{
|
{
|
||||||
return wrapper;
|
return wrapper;
|
||||||
@@ -432,12 +436,17 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task WaitStartupTaskAsync(string startupTaskKey)
|
public Task WaitStartupTaskAsync(string startupTaskKey)
|
||||||
=> GetStartupTaskWrapper(startupTaskKey).StartupTask.TaskResult;
|
=> GetStartupTaskWrapper(startupTaskKey).TaskBase.TaskResult;
|
||||||
|
|
||||||
StartupTask IStartupManager.GetStartupTask<T>()
|
StartupTaskBase IStartupManager.GetStartupTask<T>()
|
||||||
=> GetStartupTaskWrapper(StartupTypeToKey(typeof(T))).StartupTask;
|
=> GetStartupTaskWrapper(StartupTypeToKey(typeof(T))).TaskBase;
|
||||||
|
|
||||||
private static string StartupTypeToKey(Type type)
|
private static string StartupTypeToKey(Type type)
|
||||||
=> type.Name.Remove(type.Name.Length - "startup".Length);
|
=> type.Name.Remove(type.Name.Length - "startup".Length);
|
||||||
|
|
||||||
|
internal virtual Task<string> ExecuteStartupTaskAsync(StartupTaskBase startupTask, IStartupContext context, bool uiOnly)
|
||||||
|
{
|
||||||
|
return startupTask.JoinAsync(context, uiOnly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,12 +9,12 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 此启动任务的任务必须在指定的其他启动任务之前完成。可以使用 “;” 分隔符指定多个启动任务。
|
/// 此启动任务的任务必须在指定的其他启动任务之前完成。可以使用 “;” 分隔符指定多个启动任务。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BeforeTasks { get; set; }
|
public string? BeforeTasks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 效果上等同于 <see cref="BeforeTasks"/> 属性,此属性只是为了开发方便而已
|
/// 效果上等同于 <see cref="BeforeTasks"/> 属性,此属性只是为了开发方便而已
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] BeforeTaskList
|
public string[]? BeforeTaskList
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@@ -22,19 +22,19 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return BeforeTasks.Split(';');
|
return BeforeTasks?.Split(';');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 此启动任务的任务将在指定的其他启动任务之后开始执行。可以使用 “;” 分隔符指定多个启动任务。
|
/// 此启动任务的任务将在指定的其他启动任务之后开始执行。可以使用 “;” 分隔符指定多个启动任务。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AfterTasks { get; set; }
|
public string? AfterTasks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 效果上等同于 <see cref="AfterTasks"/> 属性,此属性只是为了开发方便而已
|
/// 效果上等同于 <see cref="AfterTasks"/> 属性,此属性只是为了开发方便而已
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] AfterTaskList
|
public string[]? AfterTaskList
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
}
|
}
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return AfterTasks.Split(';');
|
return AfterTasks?.Split(';');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
public abstract class StartupTask
|
public abstract class StartupTaskBase
|
||||||
{
|
{
|
||||||
// 由于我们都在编译期间收集 Attribute 了,当然也能收集使用方到底重写了哪个 Run。
|
// 由于我们都在编译期间收集 Attribute 了,当然也能收集使用方到底重写了哪个 Run。
|
||||||
// 这里传入的 isUIOnly 就是编译期间收集的那个属性。
|
// 这里传入的 isUIOnly 就是编译期间收集的那个属性。
|
||||||
@@ -38,18 +38,22 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
protected internal static Task CompletedTask =>
|
protected internal static Task CompletedTask =>
|
||||||
#if NETFRAMEWORK
|
#if NETFRAMEWORK
|
||||||
CompletedCommonTask;
|
CompletedCommonTask;
|
||||||
|
#pragma warning disable IDE0032 // Use auto property
|
||||||
private static readonly Task CompletedCommonTask = Task.FromResult(true);
|
private static readonly Task CompletedCommonTask = Task.FromResult(true);
|
||||||
|
#pragma warning restore IDE0032 // Use auto property
|
||||||
#else
|
#else
|
||||||
Task.CompletedTask;
|
Task.CompletedTask;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public Task TaskResult => CompletedSource.Task;
|
public Task TaskResult => CompletedSource.Task;
|
||||||
|
|
||||||
private TaskCompletionSource<object> CompletedSource { get; } = new TaskCompletionSource<object>();
|
private TaskCompletionSource<object?> CompletedSource { get; } = new TaskCompletionSource<object?>();
|
||||||
|
|
||||||
internal IStartupManager Manager { get; set; }
|
internal IStartupManager Manager { get; set; }
|
||||||
|
// 框架注入,一定不为空
|
||||||
|
= null!;
|
||||||
|
|
||||||
protected TValue FetchValue<TStartup, TValue>() where TStartup : StartupTask, IStartupValueProvider<TValue>
|
protected TValue FetchValue<TStartup, TValue>() where TStartup : StartupTaskBase, IStartupValueProvider<TValue>
|
||||||
{
|
{
|
||||||
var task = Manager.GetStartupTask<TStartup>();
|
var task = Manager.GetStartupTask<TStartup>();
|
||||||
var v = (IStartupValueProvider<TValue>)task;
|
var v = (IStartupValueProvider<TValue>)task;
|
||||||
@@ -2,8 +2,13 @@
|
|||||||
{
|
{
|
||||||
static class StartupTaskHelper
|
static class StartupTaskHelper
|
||||||
{
|
{
|
||||||
public static string BuildTasks(params string[] taskList)
|
public static string? BuildTasks(params string[]? taskList)
|
||||||
{
|
{
|
||||||
|
if (taskList is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (taskList.Length > 1)
|
if (taskList.Length > 1)
|
||||||
{
|
{
|
||||||
return string.Join(";", taskList);
|
return string.Join(";", taskList);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
/// 记录 <typeparamref name="TStartupTask"/> 类型中标记的从 <see cref="StartupTaskAttribute"/> 中统一收集元数据。
|
/// 记录 <typeparamref name="TStartupTask"/> 类型中标记的从 <see cref="StartupTaskAttribute"/> 中统一收集元数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TStartupTask"></typeparam>
|
/// <typeparam name="TStartupTask"></typeparam>
|
||||||
public class StartupTaskMetadata<TStartupTask> : StartupTaskMetadata where TStartupTask : StartupTask, new()
|
public class StartupTaskMetadata<TStartupTask> : StartupTaskMetadata where TStartupTask : StartupTaskBase, new()
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建包含 <typeparamref name="TStartupTask"/> 元数据的 <see cref="StartupTaskMetadata{TStartupTask}"/> 的新实例。
|
/// 创建包含 <typeparamref name="TStartupTask"/> 元数据的 <see cref="StartupTaskMetadata{TStartupTask}"/> 的新实例。
|
||||||
|
|||||||
@@ -4,54 +4,44 @@ using System.Threading;
|
|||||||
namespace dotnetCampus.ApplicationStartupManager
|
namespace dotnetCampus.ApplicationStartupManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 记录 <see cref="StartupTask"/> 类型中标记的从 <see cref="StartupTaskAttribute"/> 中统一收集元数据。
|
/// 记录 <see cref="StartupTaskBase"/> 类型中标记的从 <see cref="StartupTaskAttribute"/> 中统一收集元数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StartupTaskMetadata
|
public class StartupTaskMetadata
|
||||||
{
|
{
|
||||||
private readonly Lazy<StartupTask> _taskLazy;
|
private readonly Lazy<StartupTaskBase> _taskLazy;
|
||||||
private string _afterTasks;
|
|
||||||
private string _beforeTasks;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建 <see cref="StartupTaskMetadata"/> 的新实例。
|
/// 创建 <see cref="StartupTaskMetadata"/> 的新实例。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">表示此 <see cref="StartupTask"/> 的唯一标识符。</param>
|
/// <param name="key">表示此 <see cref="StartupTaskBase"/> 的唯一标识符。</param>
|
||||||
/// <param name="creator">此 <see cref="StartupTask"/> 实例的创建方法。</param>
|
/// <param name="creator">此 <see cref="StartupTaskBase"/> 实例的创建方法。</param>
|
||||||
public StartupTaskMetadata( string key, Func<StartupTask> creator)
|
public StartupTaskMetadata( string key, Func<StartupTaskBase> creator)
|
||||||
{
|
{
|
||||||
Key = key ?? throw new ArgumentNullException(nameof(key));
|
Key = key ?? throw new ArgumentNullException(nameof(key));
|
||||||
_taskLazy = new Lazy<StartupTask>(
|
_taskLazy = new Lazy<StartupTaskBase>(
|
||||||
creator ?? throw new ArgumentNullException(nameof(creator)),
|
creator ?? throw new ArgumentNullException(nameof(creator)),
|
||||||
LazyThreadSafetyMode.None);
|
LazyThreadSafetyMode.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 <see cref="StartupTask"/> 的唯一标识符。
|
/// 获取 <see cref="StartupTaskBase"/> 的唯一标识符。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取<see cref="StartupTaskAttribute.Categories"/> 的值,
|
/// 获取 Categories 的值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StartupCategory Categories { get; set; } = StartupCategory.All;
|
public StartupCategory Categories { get; set; } = StartupCategory.All;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 <see cref="StartupTaskAttribute.BeforeTasks"/> 的值,如果没有标记,则为 null。
|
/// 获取 <see cref="StartupTaskAttribute.BeforeTasks"/> 的值,如果没有标记,则为 null。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BeforeTasks
|
public string? BeforeTasks { get; set; }
|
||||||
{
|
|
||||||
get => _beforeTasks;
|
|
||||||
set => _beforeTasks = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 <see cref="StartupTaskAttribute.AfterTasks"/> 的值,如果没有标记,则为 null。
|
/// 获取 <see cref="StartupTaskAttribute.AfterTasks"/> 的值,如果没有标记,则为 null。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AfterTasks
|
public string? AfterTasks { get; set; }
|
||||||
{
|
|
||||||
get => _afterTasks;
|
|
||||||
set => _afterTasks = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 <see cref="StartupTaskAttribute.Scheduler"/> 的值,如果没有标记,则为 <see cref="StartupScheduler.Default"/>。
|
/// 获取 <see cref="StartupTaskAttribute.Scheduler"/> 的值,如果没有标记,则为 <see cref="StartupScheduler.Default"/>。
|
||||||
@@ -59,9 +49,9 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
public StartupScheduler Scheduler { get; set; }
|
public StartupScheduler Scheduler { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据从元数据中收集到的创建 <see cref="StartupTask"/> 的方法获取或创建 <see cref="StartupTask"/> 的实例。
|
/// 根据从元数据中收集到的创建 <see cref="StartupTaskBase"/> 的方法获取或创建 <see cref="StartupTaskBase"/> 的实例。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StartupTask Instance => _taskLazy.Value;
|
public StartupTaskBase Instance => _taskLazy.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 <see cref="StartupTaskAttribute.CriticalLevel"/> 的值,如果没有获取或设置此启动任务的关键级别,则为 <see cref="StartupCriticalLevel.Unset"/>。
|
/// 获取 <see cref="StartupTaskAttribute.CriticalLevel"/> 的值,如果没有获取或设置此启动任务的关键级别,则为 <see cref="StartupCriticalLevel.Unset"/>。
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
[DebuggerDisplay("{StartupTaskKey}:{IsVisited},{VisitedFinishTime}")]
|
[DebuggerDisplay("{StartupTaskKey}:{IsVisited},{VisitedFinishTime}")]
|
||||||
internal class StartupTaskWrapper : IStartupTaskWrapper
|
internal class StartupTaskWrapper : IStartupTaskWrapper
|
||||||
{
|
{
|
||||||
|
private readonly StartupManagerBase _manager;
|
||||||
public HashSet<string> FollowTasks { get; private set; } = new HashSet<string>();
|
public HashSet<string> FollowTasks { get; private set; } = new HashSet<string>();
|
||||||
public HashSet<string> Dependencies { get; private set; } = new HashSet<string>();
|
public HashSet<string> Dependencies { get; private set; } = new HashSet<string>();
|
||||||
|
|
||||||
@@ -20,16 +21,19 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
|
|
||||||
public StartupCategory Categories { get; internal set; } = StartupCategory.All;
|
public StartupCategory Categories { get; internal set; } = StartupCategory.All;
|
||||||
|
|
||||||
public StartupTask StartupTask { get; internal set; }
|
public StartupTaskBase TaskBase { get; internal set; }
|
||||||
|
// 框架注入,一定不为空
|
||||||
|
= null!;
|
||||||
public bool UIOnly { get; internal set; }
|
public bool UIOnly { get; internal set; }
|
||||||
public StartupCriticalLevel CriticalLevel { get; set; }
|
public StartupCriticalLevel CriticalLevel { get; set; }
|
||||||
|
|
||||||
public StartupTaskWrapper(string startupTaskKey)
|
public StartupTaskWrapper(string startupTaskKey, StartupManagerBase manager)
|
||||||
{
|
{
|
||||||
|
_manager = manager;
|
||||||
StartupTaskKey = startupTaskKey;
|
StartupTaskKey = startupTaskKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ExecuteTask(IEnumerable<StartupTask> dependencies, StartupContext context)
|
public async void ExecuteTask(IEnumerable<StartupTaskBase> dependencies, StartupContext context)
|
||||||
{
|
{
|
||||||
await Task.WhenAll(dependencies.Select(task => task.TaskResult));
|
await Task.WhenAll(dependencies.Select(task => task.TaskResult));
|
||||||
#pragma warning disable CS4014 // 由于此调用不会等待,因此在调用完成前将继续执行当前方法
|
#pragma warning disable CS4014 // 由于此调用不会等待,因此在调用完成前将继续执行当前方法
|
||||||
@@ -43,7 +47,7 @@ namespace dotnetCampus.ApplicationStartupManager
|
|||||||
//todo Tracer.Info($"[Startup]关键节点:{StartupTaskKey}开始执行");
|
//todo Tracer.Info($"[Startup]关键节点:{StartupTaskKey}开始执行");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await StartupTask.JoinAsync(context, UIOnly);
|
var result = await _manager.ExecuteStartupTaskAsync(TaskBase, context, UIOnly);
|
||||||
|
|
||||||
if (CriticalLevel == StartupCriticalLevel.Critical)
|
if (CriticalLevel == StartupCriticalLevel.Critical)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
<TargetFrameworks>netcoreapp3.1;netstandard2.0;net45;net5.0</TargetFrameworks>
|
<TargetFrameworks>netcoreapp3.1;netstandard2.0;net45;net5.0</TargetFrameworks>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<!-- <GenerateDocumentationFile>true</GenerateDocumentationFile> -->
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -41,8 +42,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="dotnetCampus.Configurations" Version="1.6.9" />
|
<!--<PackageReference Include="dotnetCampus.Configurations" Version="1.6.9" />-->
|
||||||
<PackageReference Include="dotnetCampus.SourceYard" Version="0.1.19369-alpha">
|
<PackageReference Include="dotnetCampus.SourceYard" Version="1.0.0-alpha01">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
Reference in New Issue
Block a user