diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index 8ba671fc..e99c2125 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -12,33 +12,25 @@ on:
- "dev"
jobs:
- mac-build:
- runs-on: macos-latest
+ build:
+ name: Running tests on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v3
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
- 6.0.x
- 7.0.x
- 8.0.x
- - name: build
- run: bash build.sh
-
- linux-build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - name: Setup .NET SDK
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: |
- 6.0.x
- 7.0.x
- 8.0.x
+ 6.x
+ 7.x
+ 8.x
+ - name: dotnet info
+ run: dotnet --info
- name: build
- run: bash build.sh
+ run: bash build.sh --target=test
windows-build:
runs-on: windows-latest
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 851637f7..e3dccd61 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -24,7 +24,8 @@
-
+
+
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 405c6088..e06a5a3d 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -36,4 +36,3 @@ steps:
displayName: 'Powershell Script'
env:
Nuget__ApiKey: $(nugetApiKey)
- Nuget__SourceUrl: $(nugetSourceUrl)
diff --git a/build.cake b/build.cake
deleted file mode 100644
index 9f904019..00000000
--- a/build.cake
+++ /dev/null
@@ -1,156 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// ARGUMENTS
-///////////////////////////////////////////////////////////////////////////////
-
-var target = Argument("target", "Default");
-var configuration = Argument("configuration", "Release");
-var stable = Argument("stable", "false");
-
-var branchName = EnvironmentVariable("BUILD_SOURCEBRANCHNAME") ?? "local";
-var isWindowsAgent = EnvironmentVariable("Agent_OS") == "Windows_NT" || branchName == "local";
-
-var solutionPath = "./WeihanLi.Common.sln";
-var srcProjects = GetFiles("./src/**/*.csproj");
-var testProjects = GetFiles("./test/**/*.csproj");
-var packProjects = GetFiles("./src/**/*.csproj");
-
-var artifacts = "./artifacts/packages";
-
-///////////////////////////////////////////////////////////////////////////////
-// SETUP / TEARDOWN
-///////////////////////////////////////////////////////////////////////////////
-
-Setup(ctx =>
-{
- // Executed BEFORE the first task.
- Information("Running tasks...");
- PrintBuildInfo(ctx);
-});
-
-Teardown(ctx =>
-{
- // Executed AFTER the last task.
- Information("Finished running tasks.");
-});
-
-///////////////////////////////////////////////////////////////////////////////
-// TASKS
-///////////////////////////////////////////////////////////////////////////////
-
-Task("clean")
- .Description("Clean")
- .Does(() =>
- {
- var deleteSetting = new DeleteDirectorySettings()
- {
- Force = true,
- Recursive = true
- };
- if (DirectoryExists(artifacts))
- {
- DeleteDirectory(artifacts, deleteSetting);
- }
- });
-
-Task("restore")
- .Description("Restore")
- .Does(() =>
- {
- foreach(var project in srcProjects)
- {
- DotNetRestore(project.FullPath);
- }
- });
-
-Task("build")
- .Description("Build")
- .IsDependentOn("clean")
- .IsDependentOn("restore")
- .Does(() =>
- {
- var buildSetting = new DotNetBuildSettings
- {
- NoRestore = true,
- Configuration = configuration
- };
- foreach(var project in srcProjects)
- {
- DotNetBuild(project.FullPath, buildSetting);
- }
- });
-
-
-Task("test")
- .Description("Tests")
- .IsDependentOn("build")
- .Does(() =>
- {
- var testSettings = new DotNetTestSettings
- {
- NoRestore = false,
- Configuration = "Debug"
- };
- foreach(var project in testProjects)
- {
- DotNetTest(project.FullPath, testSettings);
- }
- });
-
-Task("pack")
- .Description("Pack package")
- .IsDependentOn("test")
- .Does((cakeContext) =>
- {
- var settings = new DotNetPackSettings
- {
- Configuration = "Release",
- OutputDirectory = artifacts,
- VersionSuffix = "",
- NoRestore = true,
- };
- if(branchName != "master" && stable != "true"){
- settings.VersionSuffix = $"preview-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
- }
- foreach (var project in packProjects)
- {
- DotNetPack(project.FullPath, settings);
- }
- PublishArtifacts(cakeContext);
- });
-
-bool PublishArtifacts(ICakeContext context)
-{
- if(context.Environment.Platform.IsUnix())
- {
- Information($@"none windows build agent, do not publish packages");
- return false;
- }
- if(branchName == "master" || branchName == "preview")
- {
- var pushSetting =new DotNetNuGetPushSettings
- {
- SkipDuplicate = true,
- Source = EnvironmentVariable("Nuget__SourceUrl") ?? "https://api.nuget.org/v3/index.json",
- ApiKey = EnvironmentVariable("Nuget__ApiKey")
- };
- var packages = GetFiles($"{artifacts}/*.nupkg");
- foreach(var package in packages)
- {
- DotNetNuGetPush(package.FullPath, pushSetting);
- }
- return true;
- }
- Information($@"branch name does not match, do not publish packages");
- return false;
-}
-
-void PrintBuildInfo(ICakeContext context){
- Information($@"branch:{branchName},Platform: {context.Environment.Platform.Family}, IsUnix: {context.Environment.Platform.IsUnix()}
- BuildID:{EnvironmentVariable("BUILD_BUILDID")},BuildNumber:{EnvironmentVariable("BUILD_BUILDNUMBER")},BuildReason:{EnvironmentVariable("BUILD_REASON")}
- ");
-}
-
-Task("Default")
- .IsDependentOn("pack");
-
-RunTarget(target);
diff --git a/build.ps1 b/build.ps1
index c9fc00b4..f78ac380 100644
--- a/build.ps1
+++ b/build.ps1
@@ -1,8 +1,5 @@
-[string]$SCRIPT = '.\build.cake'
-
-# Install cake.tool
-dotnet tool install --global cake.tool
-
-Write-Host "dotnet cake $SCRIPT $ARGS" -ForegroundColor GREEN
+dotnet tool update -g dotnet-execute --prerelease
-dotnet cake $SCRIPT $CAKE_ARGS $ARGS
\ No newline at end of file
+Write-Host 'dotnet-exec ./build/build.cs "--args=$ARGS"' -ForegroundColor GREEN
+
+dotnet-exec ./build/build.cs --args $ARGS
diff --git a/build.sh b/build.sh
index 1fbcf731..fd2874ef 100644
--- a/build.sh
+++ b/build.sh
@@ -1,10 +1,7 @@
#!/bin/sh
-SCRIPT='./build.cake'
-# Install cake.tool
-dotnet tool install --global cake.tool
+dotnet tool update -g dotnet-execute --prerelease
export PATH="$PATH:$HOME/.dotnet/tools"
-echo "dotnet cake $SCRIPT $@"
-
-dotnet cake $SCRIPT "$@"
\ No newline at end of file
+echo "dotnet-exec ./build/build.cs --args $@"
+dotnet-exec ./build/build.cs --args "$@"
diff --git a/build/build.cs b/build/build.cs
new file mode 100644
index 00000000..c2000d54
--- /dev/null
+++ b/build/build.cs
@@ -0,0 +1,321 @@
+// Copyright (c) 2022-2023 Weihan Li. All rights reserved.
+// Licensed under the Apache license version 2.0 http://www.apache.org/licenses/LICENSE-2.0
+
+// r: "nuget: CliWrap, 3.6.4"
+
+using CliWrap;
+using Newtonsoft.Json;
+
+//
+var target = Guard.NotNull(Argument("target", "Default"));
+var apiKey = Argument("apiKey", "");
+var stable = ArgumentBool("stable", false);
+var noPush = ArgumentBool("noPush", false);
+var branchName = Environment.GetEnvironmentVariable("BUILD_SOURCEBRANCHNAME") ?? "local";
+
+var solutionPath = "./WeihanLi.Common.sln";
+string[] srcProjects = [
+ "./src/WeihanLi.Common/WeihanLi.Common.csproj",
+ "./src/WeihanLi.Common.Aspect.AspectCore/WeihanLi.Common.Aspect.AspectCore.csproj",
+ "./src/WeihanLi.Common.Aspect.Castle/WeihanLi.Common.Aspect.Castle.csproj",
+ "./src/WeihanLi.Common.Logging.Serilog/WeihanLi.Common.Logging.Serilog.csproj",
+ "./src/WeihanLi.Data/WeihanLi.Data.csproj",
+ "./src/WeihanLi.Extensions.Hosting/WeihanLi.Extensions.Hosting.csproj",
+];
+string[] testProjects = [ "./test/WeihanLi.Common.Test/WeihanLi.Common.Test.csproj" ];
+
+await BuildProcess.CreateBuilder()
+ .WithSetup(() =>
+ {
+ // cleanup artifacts
+ if (Directory.Exists("./artifacts/packages"))
+ Directory.Delete("./artifacts/packages", true);
+
+ // args
+ Console.WriteLine("Arguments");
+ Console.WriteLine($" {args.StringJoin(" ")}");
+
+ // dump runtime info
+ Console.WriteLine("RuntimeInfo:");
+ Console.WriteLine(ApplicationHelper.RuntimeInfo.ToJson(new JsonSerializerSettings()
+ {
+ Formatting = Formatting.Indented
+ }));
+ })
+ .WithTask("hello", b => b.WithExecution(() => Console.WriteLine("Hello dotnet-exec build")))
+ .WithTask("build", b =>
+ {
+ b.WithDescription("dotnet build")
+ .WithExecution(() => ExecuteCommandAsync($"dotnet build {solutionPath}"))
+ ;
+ })
+ .WithTask("test", b =>
+ {
+ b.WithDescription("dotnet test")
+ .WithDependency("build")
+ .WithExecution(async () =>
+ {
+ foreach (var project in testProjects)
+ {
+ await ExecuteCommandAsync($"dotnet test {project}");
+ }
+ })
+ ;
+ })
+ .WithTask("pack", b => b.WithDescription("dotnet pack")
+ .WithDependency("test")
+ .WithExecution(async () =>
+ {
+ if (stable)
+ {
+ foreach (var project in srcProjects)
+ {
+ await ExecuteCommandAsync($"dotnet pack {project} -o ./artifacts/packages");
+ }
+ }
+ else
+ {
+ var suffix = $"preview-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
+ foreach (var project in srcProjects)
+ {
+ await ExecuteCommandAsync($"dotnet pack {project} -o ./artifacts/packages --version-suffix {suffix}");
+ }
+ }
+
+ if (noPush)
+ {
+ Console.WriteLine("Skip push there's noPush specified");
+ return;
+ }
+
+ if (string.IsNullOrEmpty(apiKey))
+ {
+ // try to get apiKey from environment variable
+ apiKey = Environment.GetEnvironmentVariable("NuGet__ApiKey");
+
+ if (string.IsNullOrEmpty(apiKey))
+ {
+ Console.WriteLine("Skip push since there's no apiKey found");
+ return;
+ }
+ }
+
+ if (branchName != "master" && branchName != "preview")
+ {
+ Console.WriteLine($"Skip push since branch name {branchName} not support push packages");
+ return;
+ }
+
+ // push nuget packages
+ foreach (var file in Directory.GetFiles("./artifacts/packages/", "*.nupkg"))
+ {
+ await ExecuteCommandAsync($"dotnet nuget push {file} -k {apiKey} --skip-duplicate");
+ }
+ }))
+ .WithTask("Default", b => b.WithDependency("hello").WithDependency("pack"))
+ .Build()
+ .ExecuteAsync(target);
+
+
+bool ArgumentBool(string argumentName, bool defaultValue = default)
+{
+ var value = ArgumentInternal(argumentName);
+ if (value is null) return defaultValue;
+ if (value == string.Empty || value == "1") return true;
+ return value is "0" ? false : bool.Parse(value);
+}
+
+string? Argument(string argumentName, string? defaultValue = default)
+{
+ return ArgumentInternal(argumentName) ?? defaultValue;
+}
+
+string? ArgumentInternal(string argumentName)
+{
+ for (var i = 0; i < args.Length; i++)
+ {
+ if (args[i] == $"--{argumentName}" || args[i] == $"-{argumentName}")
+ {
+ if (((i + 1) == args.Length || args[i + 1].StartsWith('-')))
+ return string.Empty;
+
+ return args[i + 1];
+ }
+
+ if (args[i].StartsWith($"-{argumentName}="))
+ return args[i].Substring($"-{argumentName}=".Length);
+
+ if (args[i].StartsWith($"--{argumentName}="))
+ return args[i].Substring($"--{argumentName}=".Length);
+ }
+
+ return null;
+}
+
+async Task ExecuteCommandAsync(string commandText)
+{
+ Console.WriteLine($"Executing command: \n {commandText}");
+ Console.WriteLine();
+ var splits = commandText.Split([' '], 2);
+ var result = await Cli.Wrap(splits[0])
+ .WithArguments(splits.Length > 1 ? splits[1] : string.Empty)
+ .WithStandardErrorPipe(PipeTarget.ToStream(Console.OpenStandardError()))
+ .WithStandardOutputPipe(PipeTarget.ToStream(Console.OpenStandardOutput()))
+ .ExecuteAsync();
+ Console.WriteLine();
+ Console.WriteLine($"ExitCode: {result.ExitCode} ElapsedTime: {result.RunTime}");
+}
+
+file sealed class BuildProcess
+{
+ public IReadOnlyCollection Tasks { get; init; } = [];
+ public Func? Setup { private get; init; }
+ public Func? Cleanup { private get; init; }
+
+ public async Task ExecuteAsync(string target)
+ {
+ var task = Tasks.FirstOrDefault(x => x.Name == target);
+ if (task is null)
+ throw new InvalidOperationException("Invalid target to execute");
+
+ try
+ {
+ if (Setup != null)
+ await Setup.Invoke();
+
+ await ExecuteTask(task);
+ }
+ finally
+ {
+ if (Cleanup != null)
+ await Cleanup.Invoke();
+ }
+ }
+
+ private static async Task ExecuteTask(BuildTask task)
+ {
+ foreach (var dependencyTask in task.Dependencies)
+ {
+ await ExecuteTask(dependencyTask);
+ }
+
+ Console.WriteLine($"===== Task {task.Name} {task.Description} executing ======");
+ await task.ExecuteAsync();
+ Console.WriteLine($"===== Task {task.Name} {task.Description} executed ======");
+ }
+
+ public static BuildProcessBuilder CreateBuilder()
+ {
+ return new BuildProcessBuilder();
+ }
+}
+
+file sealed class BuildProcessBuilder
+{
+ private readonly List _tasks = [];
+ private Func? _setup, _cleanup;
+
+ public BuildProcessBuilder WithTask(string name, Action buildTaskConfigure)
+ {
+ var buildTaskBuilder = new BuildTaskBuilder(name);
+ buildTaskBuilder.WithTaskFinder(s => _tasks.Find(t => t.Name == s) ?? throw new InvalidOperationException($"No task found with name {s}"));
+ buildTaskConfigure.Invoke(buildTaskBuilder);
+ var task = buildTaskBuilder.Build();
+ _tasks.Add(task);
+ return this;
+ }
+
+ public BuildProcessBuilder WithSetup(Action setupFunc)
+ {
+ _setup = setupFunc.WrapTask();
+ return this;
+ }
+
+ public BuildProcessBuilder WithSetup(Func setupFunc)
+ {
+ _setup = setupFunc;
+ return this;
+ }
+
+ public BuildProcessBuilder WithCleanup(Action cleanupFunc)
+ {
+ _cleanup = cleanupFunc.WrapTask();
+ return this;
+ }
+
+ public BuildProcessBuilder WithCleanup(Func cleanupFunc)
+ {
+ _cleanup = cleanupFunc;
+ return this;
+ }
+
+ internal BuildProcess Build()
+ {
+ return new BuildProcess()
+ {
+ Tasks = _tasks,
+ Setup = _setup,
+ Cleanup = _cleanup
+ };
+ }
+}
+
+file sealed class BuildTask(string name, string? description, Func? execution = null)
+{
+ public string Name => name;
+ public string Description => description ?? name;
+
+ public IReadOnlyCollection Dependencies { get; init; } = [];
+
+ public Task ExecuteAsync() => execution?.Invoke() ?? Task.CompletedTask;
+}
+
+file sealed class BuildTaskBuilder(string name)
+{
+ private readonly string _name = name;
+
+ private string? _description;
+ private Func? _execution;
+ private readonly List _dependencies = [];
+
+ public BuildTaskBuilder WithDescription(string description)
+ {
+ _description = description;
+ return this;
+ }
+
+ public BuildTaskBuilder WithExecution(Action execution)
+ {
+ _execution = execution.WrapTask();
+ return this;
+ }
+ public BuildTaskBuilder WithExecution(Func execution)
+ {
+ _execution = execution;
+ return this;
+ }
+
+ public BuildTaskBuilder WithDependency(string dependencyTaskName)
+ {
+ if (_taskFinder is null) throw new InvalidOperationException("Dependency task name is not supported");
+
+ _dependencies.Add(_taskFinder.Invoke(dependencyTaskName));
+ return this;
+ }
+
+ private Func? _taskFinder;
+ internal BuildTaskBuilder WithTaskFinder(Func taskFinder)
+ {
+ _taskFinder = taskFinder;
+ return this;
+ }
+
+ public BuildTask Build()
+ {
+ var buildTask = new BuildTask(_name, _description, _execution)
+ {
+ Dependencies = _dependencies
+ };
+ return buildTask;
+ }
+}
diff --git a/nuget.config b/nuget.config
index 6ce97590..a4e1b7cd 100644
--- a/nuget.config
+++ b/nuget.config
@@ -1,5 +1,8 @@
+
+
+
diff --git a/samples/DotNetCoreSample/AppHostTest.cs b/samples/DotNetCoreSample/AppHostTest.cs
index 8547c87d..98d9741f 100644
--- a/samples/DotNetCoreSample/AppHostTest.cs
+++ b/samples/DotNetCoreSample/AppHostTest.cs
@@ -99,7 +99,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
_listener.Start();
var logger = _serviceProvider.GetRequiredService>();
logger.LogInformation("WebServer started");
-
+
while (!cancellationToken.IsCancellationRequested)
{
var listenerContext = await _listener.GetContextAsync();
@@ -135,7 +135,7 @@ public WebServerHostedService(IWebServer server)
{
_server = server;
}
-
+
public override async Task StopAsync(CancellationToken cancellationToken)
{
await _server.StopAsync(cancellationToken);
diff --git a/samples/DotNetCoreSample/CommandExecutorTest.cs b/samples/DotNetCoreSample/CommandExecutorTest.cs
index b3b823d5..f3ce2fe3 100644
--- a/samples/DotNetCoreSample/CommandExecutorTest.cs
+++ b/samples/DotNetCoreSample/CommandExecutorTest.cs
@@ -1,16 +1,24 @@
// Copyright (c) Weihan Li. All rights reserved.
// Licensed under the MIT license.
+using WeihanLi.Common.Helpers;
+using WeihanLi.Extensions;
using static WeihanLi.Common.Helpers.CommandExecutor;
namespace DotNetCoreSample;
-internal class CommandExecutorTest
+internal static class CommandExecutorTest
{
public static void MainTest()
{
- var result = ExecuteAndCapture("hostname");
- result.EnsureSuccessfulExitCode();
- Console.WriteLine(result.StandardOut);
+ ExecuteAndCapture("hostname")
+ .PrintOutputToConsole()
+ .EnsureSuccessExitCode();
+
+ ExecuteAndOutput("hostname").EnsureSuccessExitCode();
+
+ ExecuteAndOutputAsync("hostname").Wait();
+
+ ExecuteCommandAndOutput("hostname").EnsureSuccessExitCode();
}
}
diff --git a/samples/DotNetCoreSample/DisposeTest.cs b/samples/DotNetCoreSample/DisposeTest.cs
index 0340a652..82f008bf 100644
--- a/samples/DotNetCoreSample/DisposeTest.cs
+++ b/samples/DotNetCoreSample/DisposeTest.cs
@@ -30,7 +30,7 @@ public static void MainTest()
Name = "MainTest2"
};
Console.WriteLine(service.GetType());
-
+
service = null;
Console.WriteLine(service is null);
}
@@ -39,7 +39,7 @@ public static void MainTest()
Console.WriteLine(@$"---- {nameof(MainTest)} end");
}
-
+
public static async ValueTask MainTestAsync()
{
Console.WriteLine(@$"---- {nameof(MainTestAsync)} start");
@@ -63,7 +63,7 @@ public static async ValueTask MainTestAsync()
Name = "MainTestAsync2"
};
Console.WriteLine(service.GetType());
-
+
service = null;
Console.WriteLine(service is null);
}
@@ -77,12 +77,12 @@ public static async ValueTask MainTestAsync()
file sealed class TestService : DisposableBase
{
public required string Name { get; init; }
-
+
protected override void Dispose(bool disposing)
{
if (disposing)
{
- Console.WriteLine($@"<<{Name}>> disposes managed resources");
+ Console.WriteLine($@"<<{Name}>> disposes managed resources");
}
Console.WriteLine($@"<<{Name}>> disposes unmanaged resources");
base.Dispose(disposing);
diff --git a/samples/DotNetCoreSample/NewtonJsonFormatter.cs b/samples/DotNetCoreSample/NewtonJsonFormatter.cs
index a9108242..685f1455 100644
--- a/samples/DotNetCoreSample/NewtonJsonFormatter.cs
+++ b/samples/DotNetCoreSample/NewtonJsonFormatter.cs
@@ -11,14 +11,14 @@
namespace DotNetCoreSample;
-public sealed class NewtonJsonFormatterOptions: ConsoleFormatterOptions
+public sealed class NewtonJsonFormatterOptions : ConsoleFormatterOptions
{
}
-public sealed class NewtonJsonFormatter: ConsoleFormatter
+public sealed class NewtonJsonFormatter : ConsoleFormatter
{
public const string FormatterName = "NewtonJson";
-
+
private readonly NewtonJsonFormatterOptions _options;
public NewtonJsonFormatter(IOptions options) : base(FormatterName)
@@ -73,7 +73,7 @@ public override void Write(in LogEntry logEntry, IExternalScopeP
}
writer.WriteEndObject();
}
-
+
writer.WriteEndObject();
writer.Flush();
textWriter.WriteLine();
@@ -82,7 +82,7 @@ public override void Write(in LogEntry logEntry, IExternalScopeP
public static partial class LoggingBuilderExtensions
{
- public static ILoggingBuilder AddNewtonJsonConsole(this ILoggingBuilder loggingBuilder,
+ public static ILoggingBuilder AddNewtonJsonConsole(this ILoggingBuilder loggingBuilder,
Action? optionsConfigure = null)
{
loggingBuilder.AddConsole(options => options.FormatterName = NewtonJsonFormatter.FormatterName);
diff --git a/samples/DotNetCoreSample/Program.cs b/samples/DotNetCoreSample/Program.cs
index 006cff1c..f3a56926 100644
--- a/samples/DotNetCoreSample/Program.cs
+++ b/samples/DotNetCoreSample/Program.cs
@@ -1,11 +1,11 @@
-using System.Net.Mime;
-// Copyright (c) Weihan Li. All rights reserved.
+// Copyright (c) Weihan Li. All rights reserved.
// Licensed under the Apache license.
using DotNetCoreSample;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using System.Net.Mime;
using WeihanLi.Common;
using WeihanLi.Common.Aspect;
using WeihanLi.Common.Event;
@@ -15,6 +15,8 @@
Console.WriteLine("----------DotNetCoreSample----------");
+InvokeHelper.OnInvokeException = ex => ConsoleHelper.ErrorWriteWithColor(ex.ToString(), ConsoleColor.DarkRed);
+
// ServiceDecoratorTest.MainTest();
// var dataLogger = LogHelper.GetLogger(typeof(DataExtension));
@@ -324,18 +326,20 @@
// await AppHostTest.MainTest();
// NewtonJsonFormatterTest.MainTest();
-DisposeTest.MainTest();
-Console.WriteLine();
-await DisposeTest.MainTestAsync();
-Console.WriteLine();
-
-ConsoleHelper.ReadKeyWithPrompt("Press any key to continue");
-
-await DisposeTest.MainTestAsync();
-Console.WriteLine();
+// DisposeTest.MainTest();
+// Console.WriteLine();
+// await DisposeTest.MainTestAsync();
+// Console.WriteLine();
+//
+// ConsoleHelper.ReadKeyWithPrompt("Press any key to continue");
+//
+// await DisposeTest.MainTestAsync();
+// Console.WriteLine();
+//
+// GC.Collect();
+// GC.WaitForPendingFinalizers();
-GC.Collect();
-GC.WaitForPendingFinalizers();
+await InvokeHelper.TryInvokeAsync(TemplatingSample.MainTest);
ConsoleHelper.ReadKeyWithPrompt("Press any key to exit");
diff --git a/samples/DotNetCoreSample/TemplatingSample.cs b/samples/DotNetCoreSample/TemplatingSample.cs
index a00fd520..d774214b 100644
--- a/samples/DotNetCoreSample/TemplatingSample.cs
+++ b/samples/DotNetCoreSample/TemplatingSample.cs
@@ -1,17 +1,47 @@
// Copyright (c) Weihan Li. All rights reserved.
// Licensed under the Apache license.
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using WeihanLi.Common.Templating;
namespace DotNetCoreSample;
public class TemplatingSample
{
- public static void MainTest()
+ public static async Task MainTest()
{
- var engine = TemplateEngine.CreateDefault();
- var result = engine.RenderAsync("Hello {{Name}}", new { Name = ".NET" });
- result.Wait();
- Console.WriteLine(result.Result);
+ {
+ var engine = TemplateEngine.CreateDefault();
+ var result = await engine.RenderAsync("Hello {{Name}}", new { Name = ".NET" });
+ Console.WriteLine(result);
+ }
+
+ {
+ var result = await TemplateEngine.CreateDefault().RenderAsync("Hello {{$env USERNAME}}");
+ Console.WriteLine(result);
+ }
+
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection([new("UserName", "Test")])
+ .Build();
+ var result = await TemplateEngine.CreateDefault(builder => builder.ConfigureOptions(options => options.Configuration = configuration))
+ .RenderAsync("Hello {{$config UserName}}");
+ Console.WriteLine(result);
+ }
+
+ {
+ var services = new ServiceCollection();
+ IConfiguration configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection([new("UserName1", "Test1234")])
+ .Build();
+ services.AddSingleton(configuration);
+ services.AddTemplating();
+ await using var provider = services.BuildServiceProvider();
+ var result = await provider.GetRequiredService()
+ .RenderAsync("Hello {{$config UserName1}}");
+ Console.WriteLine(result);
+ }
}
}
diff --git a/src/WeihanLi.Common/Aspect/AspectDelegate.cs b/src/WeihanLi.Common/Aspect/AspectDelegate.cs
index 7a4af19d..e6f37179 100644
--- a/src/WeihanLi.Common/Aspect/AspectDelegate.cs
+++ b/src/WeihanLi.Common/Aspect/AspectDelegate.cs
@@ -86,7 +86,7 @@ public static void InvokeInternal(IInvocation invocation, IReadOnlyList GetAspectDelegate(IInvocation invocation, IReadOnlyList? interceptors, Func? completeFunc)
{
diff --git a/src/WeihanLi.Common/Aspect/FluentAspectOptionsExtensions.cs b/src/WeihanLi.Common/Aspect/FluentAspectOptionsExtensions.cs
index 05e73794..010e4927 100644
--- a/src/WeihanLi.Common/Aspect/FluentAspectOptionsExtensions.cs
+++ b/src/WeihanLi.Common/Aspect/FluentAspectOptionsExtensions.cs
@@ -67,7 +67,7 @@ public static IInterceptionConfiguration InterceptMethod(this FluentAspectOption
return options.InterceptMethod(m => m.GetSignature().Equals(methodSignature));
}
- public static IInterceptionConfiguration InterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]T>(this FluentAspectOptions options,
+ public static IInterceptionConfiguration InterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,
Expression> expression)
{
var prop = expression.GetProperty();
@@ -84,7 +84,7 @@ public static IInterceptionConfiguration InterceptMethod(this FluentAspectOption
return options.InterceptMethod(prop.GetMethod);
}
- public static IInterceptionConfiguration InterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]T>(this FluentAspectOptions options,
+ public static IInterceptionConfiguration InterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,
Expression> expression)
{
var prop = expression.GetProperty();
@@ -234,7 +234,7 @@ public static FluentAspectOptions NoInterceptMethod(this FluentAspectOptions opt
return options.NoInterceptMethod(m => m.GetSignature().Equals(methodSignature));
}
- public static FluentAspectOptions NoInterceptProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]T>(this FluentAspectOptions options,
+ public static FluentAspectOptions NoInterceptProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,
Expression> expression)
{
var prop = expression.GetProperty();
@@ -254,7 +254,7 @@ public static FluentAspectOptions NoInterceptMethod(this FluentAspectOptions opt
return options;
}
- public static FluentAspectOptions NoInterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]T>(this FluentAspectOptions options,
+ public static FluentAspectOptions NoInterceptPropertyGetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,
Expression> expression)
{
var prop = expression.GetProperty();
@@ -271,7 +271,7 @@ public static FluentAspectOptions NoInterceptMethod(this FluentAspectOptions opt
return options.NoInterceptMethod(prop.GetMethod);
}
- public static FluentAspectOptions NoInterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]T>(this FluentAspectOptions options,
+ public static FluentAspectOptions NoInterceptPropertySetter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this FluentAspectOptions options,
Expression> expression)
{
var prop = expression.GetProperty();
@@ -323,8 +323,8 @@ public static FluentAspectOptions UseProxyFactory(this FluentAspe
options.ProxyFactory = new TProxyFactory();
return options;
}
-
- public static FluentAspectOptions UseProxyFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TProxyFactory>(this FluentAspectOptions options,
+
+ public static FluentAspectOptions UseProxyFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TProxyFactory>(this FluentAspectOptions options,
params object[] parameters) where TProxyFactory : class, IProxyFactory
{
options.ProxyFactory = ActivatorHelper.CreateInstance(parameters);
diff --git a/src/WeihanLi.Common/Aspect/IInterceptionConfiguration.cs b/src/WeihanLi.Common/Aspect/IInterceptionConfiguration.cs
index 7afa154d..c184ebb4 100644
--- a/src/WeihanLi.Common/Aspect/IInterceptionConfiguration.cs
+++ b/src/WeihanLi.Common/Aspect/IInterceptionConfiguration.cs
@@ -40,7 +40,7 @@ public static IInterceptionConfiguration With(this IInterceptionConfiguration in
return interceptionConfiguration;
}
- public static IInterceptionConfiguration With<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TInterceptor>(this IInterceptionConfiguration interceptionConfiguration, params object?[] parameters) where TInterceptor : IInterceptor
+ public static IInterceptionConfiguration With<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TInterceptor>(this IInterceptionConfiguration interceptionConfiguration, params object?[] parameters) where TInterceptor : IInterceptor
{
if (Guard.NotNull(parameters, nameof(parameters)).Length == 0)
{
diff --git a/src/WeihanLi.Common/Aspect/ProxyUtils.cs b/src/WeihanLi.Common/Aspect/ProxyUtils.cs
index e134227e..67d9dd2e 100644
--- a/src/WeihanLi.Common/Aspect/ProxyUtils.cs
+++ b/src/WeihanLi.Common/Aspect/ProxyUtils.cs
@@ -29,7 +29,7 @@ internal static class ProxyUtils
"GetType",
"Finalize",
};
-
+
static ProxyUtils()
{
var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ProxyAssemblyName), AssemblyBuilderAccess.Run);
@@ -70,7 +70,7 @@ private static string GetFriendlyTypeName(this Type? type)
return type.IsBasicType() ? type.Name : type.FullName ?? type.Name;
}
-
+
[RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")]
[RequiresUnreferencedCode("Unreferenced code may be used")]
public static Type CreateInterfaceProxy(Type interfaceType)
@@ -315,7 +315,7 @@ public static Type CreateInterfaceProxy(Type interfaceType, Type? implementType)
public static Type CreateClassProxy(Type serviceType, Type? implementType)
{
Guard.NotNull(serviceType);
-
+
if (implementType is null)
{
implementType = serviceType;
@@ -767,7 +767,7 @@ private static GenericParameterAttributes ToClassGenericParameterAttributes(Gene
return GenericParameterAttributes.None;
}
}
-
+
[RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")]
[RequiresUnreferencedCode("Unreferenced code may be used")]
private static CustomAttributeBuilder DefineCustomAttribute(CustomAttributeData customAttributeData)
@@ -776,7 +776,7 @@ private static CustomAttributeBuilder DefineCustomAttribute(CustomAttributeData
if (customAttributeData.NamedArguments is null)
return new CustomAttributeBuilder(customAttributeData.Constructor,
customAttributeData.ConstructorArguments.Select(c => c.Value).ToArray());
-
+
var attributeTypeInfo = customAttributeData.AttributeType.GetTypeInfo();
var constructorArgs = customAttributeData.ConstructorArguments
.Select(ReadAttributeValue)
diff --git a/src/WeihanLi.Common/CacheUtil.cs b/src/WeihanLi.Common/CacheUtil.cs
index a0d6d0c0..2c8a241a 100644
--- a/src/WeihanLi.Common/CacheUtil.cs
+++ b/src/WeihanLi.Common/CacheUtil.cs
@@ -9,14 +9,14 @@ public static class CacheUtil
{
private static readonly ConcurrentDictionary TypePropertyCache = new();
private static readonly ConcurrentDictionary TypeFieldCache = new();
-
- public static PropertyInfo[] GetTypeProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]Type type)
+
+ public static PropertyInfo[] GetTypeProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type)
{
Guard.NotNull(type);
return TypePropertyCache.GetOrAdd(type, _ => type.GetProperties());
}
-
- public static FieldInfo[] GetTypeFields([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]Type type)
+
+ public static FieldInfo[] GetTypeFields([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] Type type)
{
Guard.NotNull(type);
return TypeFieldCache.GetOrAdd(type, _ => type.GetFields());
diff --git a/src/WeihanLi.Common/Data/Repository.cs b/src/WeihanLi.Common/Data/Repository.cs
index a4f89284..734e6abe 100644
--- a/src/WeihanLi.Common/Data/Repository.cs
+++ b/src/WeihanLi.Common/Data/Repository.cs
@@ -11,7 +11,7 @@
namespace WeihanLi.Common.Data;
[CLSCompliant(false)]
-public class Repository<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors|DynamicallyAccessedMemberTypes.PublicProperties)]TEntity> : IRepository where TEntity : new()
+public class Repository<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties)] TEntity> : IRepository where TEntity : new()
{
#region TODO: Cache External
diff --git a/src/WeihanLi.Common/DependencyInjection/ServiceContainer.cs b/src/WeihanLi.Common/DependencyInjection/ServiceContainer.cs
index d975e264..5cd999de 100644
--- a/src/WeihanLi.Common/DependencyInjection/ServiceContainer.cs
+++ b/src/WeihanLi.Common/DependencyInjection/ServiceContainer.cs
@@ -155,7 +155,7 @@ public void Dispose()
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
private object? GetServiceInstance(Type serviceType, ServiceDefinition serviceDefinition)
=> EnrichObject(GetServiceInstanceInternal(serviceType, serviceDefinition));
-
+
[RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")]
[RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
private object? GetServiceInstanceInternal(Type serviceType, ServiceDefinition serviceDefinition)
diff --git a/src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.cs b/src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.cs
index 2c551b05..537996f5 100644
--- a/src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.cs
+++ b/src/WeihanLi.Common/DependencyInjection/ServiceContainerBuilderExtensions.cs
@@ -7,7 +7,7 @@ namespace WeihanLi.Common.DependencyInjection;
public static partial class ServiceContainerBuilderExtensions
{
- public static IServiceContainerBuilder AddSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(this IServiceContainerBuilder serviceContainerBuilder, TService service)
+ public static IServiceContainerBuilder AddSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceContainerBuilder serviceContainerBuilder, TService service)
{
Guard.NotNull(service, nameof(service));
serviceContainerBuilder.Add(new ServiceDefinition(service!, typeof(TService)));
@@ -182,7 +182,7 @@ public static IServiceContainerBuilder RegisterAssemblyTypesAsImplementedInterfa
/// service lifetime
/// services
public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(
- this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]Type type,
+ this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type,
ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
=> RegisterTypeAsImplementedInterfaces(services, type, null, serviceLifetime);
@@ -194,7 +194,7 @@ public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(
/// interfaceTypeFilter
/// service lifetime
/// services
- public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]Type type, Func? interfaceTypeFilter, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
+ public static IServiceContainerBuilder RegisterTypeAsImplementedInterfaces(this IServiceContainerBuilder services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type, Func? interfaceTypeFilter, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
{
Guard.NotNull(type);
foreach (var interfaceType in type.GetImplementedInterfaces())
diff --git a/src/WeihanLi.Common/DependencyInjection/ServiceDefinition.cs b/src/WeihanLi.Common/DependencyInjection/ServiceDefinition.cs
index 5f2b6e4c..c9a4720c 100644
--- a/src/WeihanLi.Common/DependencyInjection/ServiceDefinition.cs
+++ b/src/WeihanLi.Common/DependencyInjection/ServiceDefinition.cs
@@ -58,27 +58,27 @@ public static ServiceDefinition Singleton(Func() where TServiceImplement : TService
+ public static ServiceDefinition Singleton() where TServiceImplement : TService
{
return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Singleton);
}
- public static ServiceDefinition Singleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>()
+ public static ServiceDefinition Singleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()
{
return new(typeof(TService), ServiceLifetime.Singleton);
}
- public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>(Func factory)
+ public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(Func factory)
{
return new(typeof(TService), factory, ServiceLifetime.Scoped);
}
- public static ServiceDefinition Scoped() where TServiceImplement : TService
+ public static ServiceDefinition Scoped() where TServiceImplement : TService
{
return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Scoped);
}
- public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TService>()
+ public static ServiceDefinition Scoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()
{
return new(typeof(TService), ServiceLifetime.Scoped);
}
@@ -88,12 +88,12 @@ public static ServiceDefinition Transient(Func()
+ public static ServiceDefinition Transient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>()
{
return new(typeof(TService), ServiceLifetime.Transient);
}
- public static ServiceDefinition Transient() where TServiceImplement : TService
+ public static ServiceDefinition Transient() where TServiceImplement : TService
{
return new(typeof(TService), typeof(TServiceImplement), ServiceLifetime.Transient);
}
diff --git a/src/WeihanLi.Common/DependencyResolver.cs b/src/WeihanLi.Common/DependencyResolver.cs
index e6e2932f..1acb744b 100644
--- a/src/WeihanLi.Common/DependencyResolver.cs
+++ b/src/WeihanLi.Common/DependencyResolver.cs
@@ -62,6 +62,7 @@ public ServiceProviderDependencyResolver(ServiceProvider serviceProvider)
return _serviceProvider.GetService(serviceType);
}
+ [RequiresUnreferencedCode("Calls WeihanLi.Common.DependencyInjectionExtensions.GetServices(Type)")]
public IEnumerable