diff --git a/README.md b/README.md index 36db6f774..922211b0d 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,7 @@ dotnet run - [Exposed roots via root arg](readme/exposed-roots-via-root-arg.md) - [Exposed generic roots](readme/exposed-generic-roots.md) - [Exposed generic roots with args](readme/exposed-generic-roots-with-args.md) +- [Serilog](readme/serilog.md) ### Applications - Console - [Schrödinger's cat](readme/Console.md) diff --git a/build/ReadmeTarget.cs b/build/ReadmeTarget.cs index 34b3264eb..d3e2c2a20 100644 --- a/build/ReadmeTarget.cs +++ b/build/ReadmeTarget.cs @@ -125,7 +125,7 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr foreach (var file in files) { var relativePath = Path.GetRelativePath(Environment.CurrentDirectory, file); - Part? part = default; + Part? part = null; var vars = new Dictionary { [VisibleKey] = "False", @@ -151,7 +151,7 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr if (str.StartsWith("*/")) { - part = default; + part = null; continue; } @@ -171,7 +171,7 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr body.AddRange(localBody.Select(i => i.Length > offset ? i[offset..].TrimEnd() : i)); offset = int.MaxValue; localBody.Clear(); - part = default; + part = null; continue; } @@ -212,6 +212,12 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr } } + if (line.TrimStart().StartsWith("//# ")) + { + localBody.Add(line.Replace("//# ", "")); + continue; + } + localBody.Add(line); } } diff --git a/readme/serilog.md b/readme/serilog.md new file mode 100644 index 000000000..0ab7397ef --- /dev/null +++ b/readme/serilog.md @@ -0,0 +1,174 @@ +#### Serilog + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Advanced/SerilogScenario.cs) + + +```c# +interface IDependency; + +class Dependency : IDependency +{ + public Dependency(ILogger log) + { + log.Information("created"); + } +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service : IService +{ + public Service( + ILogger log, + IDependency dependency) + { + Dependency = dependency; + log.Information("created"); + } + + public IDependency Dependency { get; } +} + +interface ILogger: Serilog.ILogger; + +class Logger(Serilog.ILogger logger) : ILogger +{ + private readonly Serilog.ILogger _logger = + logger.ForContext(typeof(T)); + + public void Write(LogEvent logEvent) => + _logger.Write(logEvent); +} + +partial class Composition +{ + private void Setup() => + DI.Setup(nameof(Composition)) + .Hint(Hint.Resolve, "Off") + + +Serilog.ILogger serilogLogger = CreateLogger(); +var composition = new Composition(logger: serilogLogger); +var service = composition.Root; +``` + +The following partial class will be generated: + +```c# +partial class Composition +{ + private readonly Composition _root; + private readonly Lock _lock; + + private Logger? _singletonLogger47; + private Logger? _singletonLogger48; + + private readonly Serilog.ILogger _argLogger; + + [OrdinalAttribute(10)] + public Composition(Serilog.ILogger logger) + { + _argLogger = logger ?? throw new ArgumentNullException(nameof(logger)); + _root = this; + _lock = new Lock(); + } + + internal Composition(Composition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + _argLogger = _root._argLogger; + _lock = _root._lock; + } + + public IService Root + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_root._singletonLogger48 is null) + { + using (_lock.EnterScope()) + { + if (_root._singletonLogger48 is null) + { + _root._singletonLogger48 = new Logger(_argLogger); + } + } + } + + if (_root._singletonLogger47 is null) + { + using (_lock.EnterScope()) + { + if (_root._singletonLogger47 is null) + { + _root._singletonLogger47 = new Logger(_argLogger); + } + } + } + + return new Service(_root._singletonLogger47!, new Dependency(_root._singletonLogger48!)); + } + } +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Service --|> IService + LoggerᐸServiceᐳ --|> ILoggerᐸServiceᐳ + Dependency --|> IDependency + LoggerᐸDependencyᐳ --|> ILoggerᐸDependencyᐳ + Composition ..> Service : IService Root + Service o-- "Singleton" LoggerᐸServiceᐳ : ILoggerᐸServiceᐳ + Service *-- Dependency : IDependency + LoggerᐸServiceᐳ o-- ILogger : Argument "logger" + Dependency o-- "Singleton" LoggerᐸDependencyᐳ : ILoggerᐸDependencyᐳ + LoggerᐸDependencyᐳ o-- ILogger : Argument "logger" + namespace Pure.DI.UsageTests.Advanced.SerilogScenario { + class Composition { + <> + +IService Root + } + class Dependency { + +Dependency(ILoggerᐸDependencyᐳ log) + } + class IDependency { + <> + } + class ILoggerᐸDependencyᐳ { + <> + } + class ILoggerᐸServiceᐳ { + <> + } + class IService { + <> + } + class LoggerᐸDependencyᐳ { + +Logger(ILogger logger) + } + class LoggerᐸServiceᐳ { + +Logger(ILogger logger) + } + class Service { + +Service(ILoggerᐸServiceᐳ log, IDependency dependency) + } + } + namespace Serilog { + class ILogger { + <> + } + } +``` + diff --git a/tests/Pure.DI.UsageTests/Advanced/SerilogScenario.cs b/tests/Pure.DI.UsageTests/Advanced/SerilogScenario.cs new file mode 100644 index 000000000..c04ce66d7 --- /dev/null +++ b/tests/Pure.DI.UsageTests/Advanced/SerilogScenario.cs @@ -0,0 +1,104 @@ +/* +$v=true +$p=301 +$d=Serilog +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedType.Global +// ReSharper disable ArrangeTypeModifiers +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable UnusedVariable + +// ReSharper disable UnusedTypeParameter +// ReSharper disable UnusedMember.Local +// ReSharper disable UnusedMemberInSuper.Global +#pragma warning disable CS9113 // Parameter is unread. +namespace Pure.DI.UsageTests.Advanced.SerilogScenario; + +using Serilog.Core; +using Serilog.Events; +using Xunit; + +// { +interface IDependency; + +class Dependency : IDependency +{ + public Dependency(ILogger log) + { + log.Information("created"); + } +} + +interface IService +{ + IDependency Dependency { get; } +} + +class Service : IService +{ + public Service( + ILogger log, + IDependency dependency) + { + Dependency = dependency; + log.Information("created"); + } + + public IDependency Dependency { get; } +} + +interface ILogger: Serilog.ILogger; + +class Logger(Serilog.ILogger logger) : ILogger +{ + private readonly Serilog.ILogger _logger = + logger.ForContext(typeof(T)); + + public void Write(LogEvent logEvent) => + _logger.Write(logEvent); +} + +partial class Composition +{ + private void Setup() => + DI.Setup(nameof(Composition)) +// { + .Hint(Hint.Resolve, "Off") +// } + .Arg("logger") + .Bind().As(Lifetime.Singleton).To>() + + .Bind().To() + .Bind().To() + .Root(nameof(Root)); +} +// } + +class EventSink(ICollection events) + : ILogEventSink +{ + public void Emit(LogEvent logEvent) => + events.Add(logEvent); +} + +public class Scenario +{ + [Fact] + public void Run() + { + var events = new List(); + var serilogLogger = new Serilog.LoggerConfiguration() + .WriteTo.Sink(new EventSink(events)) + .CreateLogger(); +// { + //# Serilog.ILogger serilogLogger = CreateLogger(); + var composition = new Composition(logger: serilogLogger); + var service = composition.Root; +// } + events.Count.ShouldBe(2); + composition.SaveClassDiagram(); + } +} \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj b/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj index fadcfdad1..e2742561a 100644 --- a/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj +++ b/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj @@ -15,6 +15,7 @@ +