diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index fa5ba2d..a75e4dd 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -4,6 +4,9 @@ on: push: branches: - master + pull_request: + branches: + - master jobs: @@ -18,7 +21,8 @@ jobs: env: Configuration: Release Solution_Path: SystemExtensions.sln - Test_Project_Path: SystemExtensions.Tests\SystemExtensions.Tests.csproj + Test_Project_Path: SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj + DI_Test_Project_Path: SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj Source_Project_Path: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj Actions_Allow_Unsecure_Commands: true @@ -41,12 +45,20 @@ jobs: env: RuntimeIdentifier: win-${{ matrix.targetplatform }} - - name: Build Slim project + - name: Build SystemExtensions.NetStandard project run: dotnet build SystemExtensions.NetStandard -c $env:Configuration + - name: Build SystemExtensions.NetStandard.DependencyInjection project + run: dotnet build SystemExtensions.NetStandard.DependencyInjection -c $env:Configuration + - name: Push nuget package uses: brandedoutcast/publish-nuget@v2.5.5 with: PROJECT_FILE_PATH: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj NUGET_KEY: ${{secrets.NUGET_API_KEY}} - \ No newline at end of file + + - name: Push nuget package + uses: brandedoutcast/publish-nuget@v2.5.5 + with: + PROJECT_FILE_PATH: SystemExtensions.NetStandard.DependencyInjection\SystemExtensions.NetStandard.DependencyInjection.csproj + NUGET_KEY: ${{secrets.NUGET_API_KEY}} \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5d3b52d..6282ce8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,8 @@ jobs: env: Solution_Path: SystemExtensions.sln - Test_Project_Path: SystemExtensions.Tests\SystemExtensions.Tests.csproj + Test_Project_Path: SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj + DI_Test_Project_Path: SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj Source_Project_Path: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj Actions_Allow_Unsecure_Commands: true @@ -41,6 +42,9 @@ jobs: - name: Execute Unit Tests run: dotnet test $env:Test_Project_Path + - name: Execute DI Unit Tests + run: dotnet test $env:DI_Test_Project_Path + - name: Restore Project run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier env: diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/DefaultOptionsManagerTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/DefaultOptionsManagerTests.cs new file mode 100644 index 0000000..20ddbc2 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/DefaultOptionsManagerTests.cs @@ -0,0 +1,26 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class DefaultOptionsManagerTests + { + private readonly DefaultOptionsManager optionsManager = new(); + + [TestMethod] + public void GetOptions_ReturnsDefault() + { + var options = this.optionsManager.GetOptions(); + + options.Should().BeNull(); + } + + [TestMethod] + public void UpdateOptions_Succeeds() + { + this.optionsManager.UpdateOptions(string.Empty); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/DummyOptions.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/DummyOptions.cs new file mode 100644 index 0000000..06830f9 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/DummyOptions.cs @@ -0,0 +1,6 @@ +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + public sealed class DummyOptions + { + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/LiveOptionsResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveOptionsResolverTests.cs new file mode 100644 index 0000000..a486e19 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveOptionsResolverTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class LiveOptionsResolverTests + { + private readonly LiveOptionsResolver liveOptionsResolver = new(); + private readonly Mock serviceProviderMock = new(); + private readonly Mock optionsManagerMock = new(); + + [TestMethod] + public void CanResolve_ILiveOptions_ReturnsTrue() + { + var type = typeof(ILiveOptions); + + var canResolve = this.liveOptionsResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(object), typeof(string), typeof(LiveOptionsResolverTests), typeof(int) }; + + foreach (var type in types) + { + var canResolve = this.liveOptionsResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_ILiveOptions_ReturnsILiveOptions() + { + this.SetupServiceProvider(); + + var liveOptions = this.liveOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(ILiveOptions)); + + liveOptions.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_AnythingElse_Throws() + { + this.SetupServiceProvider(); + + Action action = new(() => + { + this.liveOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + + private void SetupServiceProvider() + { + this.serviceProviderMock + .Setup(u => u.GetService()) + .Returns(this.optionsManagerMock.Object); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsResolverTests.cs new file mode 100644 index 0000000..e019aa1 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsResolverTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class LiveUpdateableOptionsResolverTests + { + private readonly LiveUpdateableOptionsResolver liveUpdateableOptionsResolver = new(); + private readonly Mock serviceProviderMock = new(); + private readonly Mock optionsManagerMock = new(); + + [TestMethod] + public void CanResolve_ILiveUpdateableOptions_ReturnsTrue() + { + var type = typeof(ILiveUpdateableOptions); + + var canResolve = this.liveUpdateableOptionsResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(object), typeof(string), typeof(LiveOptionsResolverTests), typeof(int) }; + + foreach (var type in types) + { + var canResolve = this.liveUpdateableOptionsResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_ILiveUpdateableOptions_ReturnsILiveUpdateableOptions() + { + this.SetupServiceProvider(); + + var liveOptions = this.liveUpdateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(ILiveUpdateableOptions)); + + liveOptions.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_AnythingElse_Throws() + { + this.SetupServiceProvider(); + + Action action = new(() => + { + this.liveUpdateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + + private void SetupServiceProvider() + { + this.serviceProviderMock + .Setup(u => u.GetService()) + .Returns(this.optionsManagerMock.Object); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsWrapperTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsWrapperTests.cs new file mode 100644 index 0000000..0399ddc --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/LiveUpdateableOptionsWrapperTests.cs @@ -0,0 +1,44 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class LiveUpdateableOptionsWrapperTests + { + private LiveUpdateableOptionsWrapper optionsWrapper; + private readonly Mock optionsManagerMock = new(); + + [TestInitialize] + public void TestInitialize() + { + this.optionsWrapper = new LiveUpdateableOptionsWrapper(this.optionsManagerMock.Object); + } + + [TestMethod] + public void GetValue_ReturnsValue() + { + this.optionsManagerMock + .Setup(u => u.GetOptions()) + .Returns("hello"); + + var value = this.optionsWrapper.Value; + + value.Should().Be("hello"); + } + + [TestMethod] + public void UpdateOption_CallsOptionsManager() + { + this.optionsManagerMock + .Setup(u => u.UpdateOptions(It.IsAny())) + .Verifiable(); + + this.optionsWrapper.UpdateOption(); + + this.optionsManagerMock.Verify(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/OptionsResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/OptionsResolverTests.cs new file mode 100644 index 0000000..9d76fd0 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/OptionsResolverTests.cs @@ -0,0 +1,72 @@ +using FluentAssertions; +using Microsoft.Extensions.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class OptionsResolverTests + { + private readonly OptionsResolver optionsResolver = new(); + private readonly Mock serviceProviderMock = new(); + private readonly Mock optionsManagerMock = new(); + + public Mock OptionsManagerMock => optionsManagerMock; + + [TestMethod] + public void CanResolve_ILiveOptions_ReturnsTrue() + { + var type = typeof(IOptions); + + var canResolve = this.optionsResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(object), typeof(string), typeof(OptionsResolverTests), typeof(int) }; + + foreach (var type in types) + { + var canResolve = this.optionsResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_IOptions_ReturnsIOptions() + { + this.SetupServiceProvider(); + + var liveOptions = this.optionsResolver.Resolve(this.serviceProviderMock.Object, typeof(IOptions)); + + liveOptions.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_AnythingElse_Throws() + { + this.SetupServiceProvider(); + + Action action = new(() => + { + this.optionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + + private void SetupServiceProvider() + { + this.serviceProviderMock + .Setup(u => u.GetService()) + .Returns(this.OptionsManagerMock.Object); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsResolverTests.cs new file mode 100644 index 0000000..e1161c2 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsResolverTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class UpdateableOptionsResolverTests + { + private readonly UpdateableOptionsResolver updateableOptionsResolver = new(); + private readonly Mock serviceProviderMock = new(); + private readonly Mock optionsManagerMock = new(); + + [TestMethod] + public void CanResolve_ILiveOptions_ReturnsTrue() + { + var type = typeof(IUpdateableOptions); + + var canResolve = this.updateableOptionsResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(object), typeof(string), typeof(UpdateableOptionsResolverTests), typeof(int) }; + + foreach (var type in types) + { + var canResolve = this.updateableOptionsResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_IUpdateableOptions_ReturnsIUpdateableOptions() + { + this.SetupServiceProvider(); + + var liveOptions = this.updateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(IUpdateableOptions)); + + liveOptions.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_AnythingElse_Throws() + { + this.SetupServiceProvider(); + + Action action = new(() => + { + this.updateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + + private void SetupServiceProvider() + { + this.serviceProviderMock + .Setup(u => u.GetService()) + .Returns(this.optionsManagerMock.Object); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsWrapperTests.cs b/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsWrapperTests.cs new file mode 100644 index 0000000..f25d4ef --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Configuration/UpdateableOptionsWrapperTests.cs @@ -0,0 +1,47 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Extensions.Configuration; + +namespace SystemExtensions.DependencyInjection.Tests.Configuration +{ + [TestClass] + public class UpdateableOptionsWrapperTests + { + private const string Value = "hello"; + + private UpdateableOptionsWrapper optionsWrapper; + private readonly Mock optionsManagerMock = new(); + + [TestInitialize] + public void TestInitialize() + { + this.optionsWrapper = new UpdateableOptionsWrapper(optionsManagerMock.Object, Value); + } + + [TestMethod] + public void GetValue_ReturnsValue() + { + this.optionsManagerMock + .Setup(u => u.GetOptions()) + .Throws(); + + var value = this.optionsWrapper.Value; + + value.Should().Be(Value); + } + + [TestMethod] + public void UpdateOption_CallsOptionsManager() + { + this.optionsManagerMock + .Setup(u => u.UpdateOptions(It.IsAny())) + .Verifiable(); + + this.optionsWrapper.UpdateOption(); + + this.optionsManagerMock.Verify(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs new file mode 100644 index 0000000..9ba798c --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Http; +using System.Net.Http; + +namespace SystemExtensions.DependencyInjection.Tests.Http +{ + [TestClass] + public class HttpClientResolverTests + { + private readonly HttpClientResolver httpClientResolver = new(); + private readonly Mock serviceProviderMock = new(); + + [TestMethod] + public void CanResolve_IHttpClient_ReturnsTrue() + { + var type = typeof(IHttpClient<>); + + var canResolve = this.httpClientResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(HttpClient), typeof(object), typeof(string), typeof(HttpClientResolverTests), typeof(int) }; + + foreach (var type in types) + { + var canResolve = this.httpClientResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_TypedClient_ReturnsIHttpClient() + { + var client = this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(IHttpClient)); + + client.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_NonGenericType_Throws() + { + Action action = new(() => + { + this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(IHttpClient<>)); + }); + + action.Should().Throw(); + } + + [TestMethod] + public void Resolve_RandomType_Throws() + { + Action action = new(() => + { + this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerProviderTests.cs b/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerProviderTests.cs new file mode 100644 index 0000000..03a6efc --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerProviderTests.cs @@ -0,0 +1,43 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System.Logging; + +namespace SystemExtensions.DependencyInjection.Tests.Logging +{ + [TestClass] + public class CVLoggerProviderTests + { + private readonly Mock logsWriterMock = new(); + private CVLoggerProvider cVLoggerProvider; + + [TestInitialize] + public void TestInitialize() + { + this.cVLoggerProvider = new CVLoggerProvider(this.logsWriterMock.Object); + } + + [TestMethod] + public void CreateLogger_CreatesNewLogger() + { + var logger = this.cVLoggerProvider.CreateLogger(string.Empty); + + logger.Should().NotBeNull(); + } + + [TestMethod] + public void LogEntry_CallsLogWriter() + { + this.cVLoggerProvider.LogEntry(new Log()); + + this.logsWriterMock.Verify(); + } + + private void SetupLogsWriter() + { + this.logsWriterMock + .Setup(u => u.WriteLog(It.IsAny())) + .Verifiable(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerTests.cs b/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerTests.cs new file mode 100644 index 0000000..91f2657 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Logging/CVLoggerTests.cs @@ -0,0 +1,76 @@ +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Logging; + +namespace SystemExtensions.DependencyInjection.Tests.Logging +{ + [TestClass] + public class CVLoggerTests + { + private readonly Mock cvLoggerProviderMock = new(); + private CVLogger cVLogger; + + [TestInitialize] + public void TestInitialize() + { + this.cVLogger = new CVLogger(string.Empty, this.cvLoggerProviderMock.Object); + } + + [TestMethod] + public void BeginScope_ReturnsNull() + { + var scope = this.cVLogger.BeginScope(string.Empty); + + scope.Should().BeNull(); + } + + [TestMethod] + [DataRow(LogLevel.Debug)] + [DataRow(LogLevel.Trace)] + [DataRow(LogLevel.Information)] + [DataRow(LogLevel.Warning)] + [DataRow(LogLevel.Error)] + [DataRow(LogLevel.Critical)] + public void IsEnabled_OnAllLogLevels_ReturnsTrue(LogLevel logLevel) + { + var enabled = this.cVLogger.IsEnabled(logLevel); + + enabled.Should().BeTrue(); + } + + [TestMethod] + public void Log_CallsFormatter() + { + var called = false; + Func messageFormatter = new((state, exception) => + { + called = true; + return string.Empty; + }); + + this.cVLogger.Log(LogLevel.Debug, new EventId(), "Some message", new Exception(), messageFormatter); + + called.Should().BeTrue(); + } + + [TestMethod] + public void Log_CallsLogsProvider() + { + this.SetupLoggerProvider(); + + this.cVLogger.Log(LogLevel.Debug, new EventId(), "Some message", new Exception(), new Func((s, e) => string.Empty)); + + this.cvLoggerProviderMock.Verify(); + } + + private void SetupLoggerProvider() + { + this.cvLoggerProviderMock + .Setup(u => u.LogEntry(It.IsAny())) + .Verifiable(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Logging/DebugLogsWriterTests.cs b/SystemExtensions.DependencyInjection.Tests/Logging/DebugLogsWriterTests.cs new file mode 100644 index 0000000..8a0a4c4 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Logging/DebugLogsWriterTests.cs @@ -0,0 +1,30 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Logging; + +namespace SystemExtensions.DependencyInjection.Tests.Logging +{ + [TestClass] + public class DebugLogsWriterTests + { + private readonly DebugLogsWriter logsWriter = new(); + + [TestMethod] + public void WriteLog_Succeeds() + { + this.logsWriter.WriteLog(new Log()); + } + + [TestMethod] + public void WriteNullLog_Throws() + { + Action action = new(() => + { + this.logsWriter.WriteLog(null); + }); + + action.Should().Throw(); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/Logging/LoggerResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Logging/LoggerResolverTests.cs new file mode 100644 index 0000000..b215ea6 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/Logging/LoggerResolverTests.cs @@ -0,0 +1,109 @@ +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Logging; +using System.Windows.Extensions.Logging; + +namespace SystemExtensions.DependencyInjection.Tests.Logging +{ + [TestClass] + public class LoggerResolverTests + { + private readonly Mock serviceProviderMock = new(); + private readonly Mock loggerFactoryMock = new(); + private readonly Mock loggerMock = new(); + private readonly LoggerResolver loggerResolver = new(); + + [TestMethod] + public void CanResolve_ILogger_ReturnsTrue() + { + var type = typeof(ILogger); + + var canResolve = this.loggerResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_GenericILogger_ReturnsTrue() + { + var type = typeof(ILogger); + + var canResolve = this.loggerResolver.CanResolve(type); + + canResolve.Should().BeTrue(); + } + + [TestMethod] + public void CanResolve_AnythingElse_ReturnsFalse() + { + var types = new Type[] { typeof(CVLogger), typeof(object), typeof(string), typeof(LoggerResolverTests), typeof(int) }; + + foreach(var type in types) + { + var canResolve = this.loggerResolver.CanResolve(type); + + canResolve.Should().BeFalse(); + } + } + + [TestMethod] + public void Resolve_ILogger_ReturnsILogger() + { + this.SetupIServiceProvider(); + + var logger = this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger)); + + logger.Should().BeAssignableTo(); + } + + [TestMethod] + public void Resolve_TypedILogger_ReturnsTypedILogger() + { + this.SetupIServiceProvider(); + + var logger = this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger)); + + logger.Should().BeAssignableTo>(); + } + + [TestMethod] + public void Resolve_RandomType_Throws() + { + this.SetupIServiceProvider(); + + Action action = new(() => + { + this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(string)); + }); + + action.Should().Throw(); + } + + [TestMethod] + public void Resolve_NonTypedGeneric_Throws() + { + this.SetupIServiceProvider(); + + Action action = new(() => + { + this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger<>)); + }); + + action.Should().Throw(); + } + + private void SetupIServiceProvider() + { + this.serviceProviderMock + .Setup(u => u.GetService()) + .Returns(this.loggerFactoryMock.Object); + + this.loggerFactoryMock + .Setup(u => u.CreateLogger(It.IsAny())) + .Returns(this.loggerMock.Object); + } + } +} diff --git a/SystemExtensions.DependencyInjection.Tests/SystemExtensions.NetStandard.DependencyInjection.Tests.csproj b/SystemExtensions.DependencyInjection.Tests/SystemExtensions.NetStandard.DependencyInjection.Tests.csproj new file mode 100644 index 0000000..2b581e8 --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/SystemExtensions.NetStandard.DependencyInjection.Tests.csproj @@ -0,0 +1,22 @@ + + + + net5.0 + + false + + + + + + + + + + + + + + + + diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/DefaultOptionsManager.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/DefaultOptionsManager.cs new file mode 100644 index 0000000..979544f --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/DefaultOptionsManager.cs @@ -0,0 +1,14 @@ +namespace System.Extensions.Configuration +{ + public sealed class DefaultOptionsManager : IOptionsManager + { + public T GetOptions() where T : class + { + return default; + } + + public void UpdateOptions(T value) where T : class + { + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveOptions.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveOptions.cs new file mode 100644 index 0000000..554167f --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveOptions.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Options; + +namespace System.Extensions.Configuration +{ + public interface ILiveOptions : IOptions + where T : class + { + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveUpdateableOptions.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveUpdateableOptions.cs new file mode 100644 index 0000000..0a5842a --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/ILiveUpdateableOptions.cs @@ -0,0 +1,7 @@ +namespace System.Extensions.Configuration +{ + public interface ILiveUpdateableOptions : ILiveOptions, IUpdateableOptions + where T : class + { + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/IOptionsManager.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/IOptionsManager.cs new file mode 100644 index 0000000..c91554c --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/IOptionsManager.cs @@ -0,0 +1,10 @@ +namespace System.Extensions.Configuration +{ + public interface IOptionsManager + { + T GetOptions() + where T : class; + void UpdateOptions(T value) + where T : class; + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/IUpdateableOptions.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/IUpdateableOptions.cs new file mode 100644 index 0000000..f9ec304 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/IUpdateableOptions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Options; + +namespace System.Extensions.Configuration +{ + public interface IUpdateableOptions : IOptions + where T : class + { + /// + /// Updates the configuration with the current value stored in . + /// + void UpdateOption(); + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveOptionsResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveOptionsResolver.cs new file mode 100644 index 0000000..bde4567 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveOptionsResolver.cs @@ -0,0 +1,27 @@ +using Slim.Resolvers; + +namespace System.Extensions.Configuration +{ + public sealed class LiveOptionsResolver : IDependencyResolver + { + private static readonly Type optionsType = typeof(LiveUpdateableOptionsWrapper<>); + + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILiveOptions<>)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments()); + var configurationManager = serviceProvider.GetService(); + + return Activator.CreateInstance(typedOptionsType, configurationManager); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsResolver.cs new file mode 100644 index 0000000..f3cd19a --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsResolver.cs @@ -0,0 +1,26 @@ +using Slim.Resolvers; + +namespace System.Extensions.Configuration +{ + public sealed class LiveUpdateableOptionsResolver : IDependencyResolver + { + private static readonly Type optionsType = typeof(LiveUpdateableOptionsWrapper<>); + + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILiveUpdateableOptions<>)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments()); + var configurationManager = serviceProvider.GetService(); + return Activator.CreateInstance(typedOptionsType, configurationManager); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsWrapper.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsWrapper.cs new file mode 100644 index 0000000..e75f172 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/LiveUpdateableOptionsWrapper.cs @@ -0,0 +1,31 @@ +using System.Extensions; + +namespace System.Extensions.Configuration +{ + public sealed class LiveUpdateableOptionsWrapper : ILiveUpdateableOptions + where T : class + { + private readonly IOptionsManager configurationManager; + + private T value; + + public T Value + { + get + { + this.value = this.configurationManager.GetOptions(); + return this.value; + } + } + + public LiveUpdateableOptionsWrapper(IOptionsManager configurationManager) + { + this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager)); + } + + public void UpdateOption() + { + this.configurationManager.UpdateOptions(this.value); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/OptionsResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/OptionsResolver.cs new file mode 100644 index 0000000..049c2bf --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/OptionsResolver.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Options; +using Slim.Resolvers; + +namespace System.Extensions.Configuration +{ + public sealed class OptionsResolver : IDependencyResolver + { + private static readonly Type optionsType = typeof(OptionsWrapper<>); + private static readonly Type configurationType = typeof(IOptionsManager); + + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IOptions<>)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments()); + var configurationManager = serviceProvider.GetService(); + var optionsValue = configurationType + .GetMethod(nameof(IOptionsManager.GetOptions)) + .MakeGenericMethod(type.GetGenericArguments()) + .Invoke(configurationManager, Array.Empty()); + + return Activator.CreateInstance(typedOptionsType, optionsValue); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsResolver.cs new file mode 100644 index 0000000..7b1cd4f --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsResolver.cs @@ -0,0 +1,32 @@ +using Slim.Resolvers; + +namespace System.Extensions.Configuration +{ + public sealed class UpdateableOptionsResolver : IDependencyResolver + { + private static readonly Type optionsType = typeof(UpdateableOptionsWrapper<>); + private static readonly Type configurationType = typeof(IOptionsManager); + + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IUpdateableOptions<>)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments()); + var configurationManager = serviceProvider.GetService(); + var optionsValue = configurationType + .GetMethod(nameof(IOptionsManager.GetOptions)) + .MakeGenericMethod(type.GetGenericArguments()) + .Invoke(configurationManager, Array.Empty()); + + return Activator.CreateInstance(typedOptionsType, configurationManager, optionsValue); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsWrapper.cs b/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsWrapper.cs new file mode 100644 index 0000000..f6b0b9c --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Configuration/UpdateableOptionsWrapper.cs @@ -0,0 +1,23 @@ +using System.Extensions; + +namespace System.Extensions.Configuration +{ + public sealed class UpdateableOptionsWrapper : IUpdateableOptions + where T : class + { + private readonly IOptionsManager configurationManager; + + public T Value { get; } + + public UpdateableOptionsWrapper(IOptionsManager configurationManager, T options) + { + this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager)); + this.Value = options; + } + + public void UpdateOption() + { + this.configurationManager.UpdateOptions(this.Value); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs b/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs new file mode 100644 index 0000000..3679e63 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs @@ -0,0 +1,142 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Slim; +using System.Extensions; +using System.Extensions.Configuration; +using System.Http; +using System.Logging; +using System.Net.Http; + +namespace System.Extensions +{ + public static class ServiceManagerExtensions + { + /// + /// Registers a with the default . + /// This call also registers the resolver that resolves and . + /// + /// . + /// Provided . + public static IServiceManager RegisterOptionsManager(this IServiceManager serviceManager) + { + serviceManager.RegisterSingleton(); + serviceManager.RegisterOptionsResolver(); + + return serviceManager; + } + + /// + /// Registers a . + /// This call also registers the resolver that resolves and . + /// + /// Implementation of . + /// . + /// Provided . + public static IServiceManager RegisterOptionsManager(this IServiceManager serviceManager) + where T : IOptionsManager + { + serviceManager.RegisterSingleton(); + serviceManager.RegisterOptionsResolver(); + + return serviceManager; + } + + /// + /// Registers resolvers for and . + /// Depends on a in order to properly resolve options. + /// + /// . + /// . + public static IServiceManager RegisterOptionsResolver(this IServiceManager serviceManager) + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterResolver(new OptionsResolver()); + serviceManager.RegisterResolver(new UpdateableOptionsResolver()); + serviceManager.RegisterResolver(new LiveOptionsResolver()); + serviceManager.RegisterResolver(new LiveUpdateableOptionsResolver()); + + return serviceManager; + } + + /// + /// Registers a with the default . + /// + /// Implementation of . + /// . + /// Provided . + public static IServiceProducer RegisterLogWriter(this IServiceProducer serviceManager) + where TLogsWriter : ILogsWriter + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterSingleton(); + serviceManager.RegisterScoped(sp => + { + var factory = new LoggerFactory(); + factory.AddProvider(new CVLoggerProvider(sp.GetService())); + return factory; + }); + + return serviceManager; + } + + /// + /// Registers a with the default . + /// + /// Interface of . + /// Implementation of . + /// . + /// Provided . + public static IServiceProducer RegisterLogWriter(this IServiceProducer serviceManager) + where TLogsWriter : TILogsWriter + where TILogsWriter : class, ILogsWriter + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterSingleton(); + serviceManager.RegisterScoped(sp => + { + var factory = new LoggerFactory(); + factory.AddProvider(new CVLoggerProvider(sp.GetService())); + return factory; + }); + + return serviceManager; + } + + public static IServiceManager RegisterLoggerFactory(this IServiceManager serviceManager, Func loggerFactory) + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterSingleton(loggerFactory); + return serviceManager; + } + + public static IServiceManager RegisterDebugLoggerFactory(this IServiceManager serviceManager) + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterLogWriter(); + return serviceManager; + } + + public static IServiceManager RegisterHttpFactory(this IServiceManager serviceManager) + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterResolver(new HttpClientResolver()); + return serviceManager; + } + + public static IServiceManager RegisterHttpFactory(this IServiceManager serviceManager, Func handlerFactory) + { + serviceManager.ThrowIfNull(nameof(serviceManager)); + + serviceManager.RegisterResolver( + new HttpClientResolver() + .WithHttpMessageHandlerFactory(handlerFactory)); + return serviceManager; + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Http/HttpClientResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Http/HttpClientResolver.cs new file mode 100644 index 0000000..6cab276 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Http/HttpClientResolver.cs @@ -0,0 +1,47 @@ +using Slim.Resolvers; +using System.Linq; +using System.Net.Http; + +namespace System.Http +{ + public sealed class HttpClientResolver : IDependencyResolver + { + private static readonly Type clientType = typeof(HttpClient<>); + + /// + /// Factory method. parameter of the factory is the scope of . + /// + public Func HttpMessageHandlerFactory { get; set; } + + /// + /// Sets the . + /// + /// Factory method. parameter of the factory is the scope of . + /// + public HttpClientResolver WithHttpMessageHandlerFactory(Func factory) + { + this.HttpMessageHandlerFactory = factory; + return this; + } + + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IHttpClient<>)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + var typedClientType = clientType.MakeGenericType(type.GetGenericArguments()); + var handler = this.HttpMessageHandlerFactory?.Invoke(serviceProvider, type.GetGenericArguments().First()); + var httpClient = handler is null ? + Activator.CreateInstance(typedClientType) : + Activator.CreateInstance(typedClientType, new object[] { handler }); + return httpClient; + } + } +} \ No newline at end of file diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLogger.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLogger.cs new file mode 100644 index 0000000..97e3646 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLogger.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Logging; +using System.Extensions; + +namespace System.Logging +{ + public sealed class CVLogger : ILogger + { + private readonly string category; + private readonly ICVLoggerProvider cvLoggerProvider; + + public CVLogger(string category, ICVLoggerProvider cvLoggerProvider) + { + this.category = category; + this.cvLoggerProvider = cvLoggerProvider.ThrowIfNull(nameof(cvLoggerProvider)); + } + + public IDisposable BeginScope(TState state) + { + return null; + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + var message = formatter(state, exception); + + var log = new Log + { + Exception = exception, + LogLevel = logLevel, + EventId = eventId.Name, + Message = message, + Category = category, + LogTime = DateTime.Now + }; + + this.cvLoggerProvider.LogEntry(log); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLoggerProvider.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLoggerProvider.cs new file mode 100644 index 0000000..5aba840 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/CVLoggerProvider.cs @@ -0,0 +1,44 @@ +using Microsoft.CorrelationVector; +using Microsoft.Extensions.Logging; +using System.Extensions; + +namespace System.Logging +{ + public sealed class CVLoggerProvider : ICVLoggerProvider + { + private readonly ILogsWriter logsManager; + private CorrelationVector correlationVector; + + public CVLoggerProvider(ILogsWriter logsWriter) + { + this.logsManager = logsWriter.ThrowIfNull(nameof(logsWriter)); + this.correlationVector = new CorrelationVector(); + } + + public void LogEntry(Log log) + { + if (this.correlationVector is not null) + { + log.CorrelationVector = this.correlationVector.Value.ToString(); + this.correlationVector.Increment(); + } + + this.logsManager.WriteLog(log); + } + + public ILogger CreateLogger(string categoryName) + { + if (this.correlationVector is not null) + { + this.correlationVector = CorrelationVector.Extend(this.correlationVector.ToString()); + } + + return new CVLogger(categoryName, this); + } + + public void Dispose() + { + throw new System.NotImplementedException(); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/DebugLogsWriter.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/DebugLogsWriter.cs new file mode 100644 index 0000000..3f57ea2 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/DebugLogsWriter.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace System.Logging +{ + public sealed class DebugLogsWriter : ILogsWriter + { + public void WriteLog(Log log) + { + Debug.WriteLine($"{log.LogTime} - {log.Category} - {log.EventId} - {log.CorrelationVector} - {log.LogLevel} - {log.Message}{Environment.NewLine}{log.Exception}"); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/ICVLoggerProvider.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/ICVLoggerProvider.cs new file mode 100644 index 0000000..41343aa --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/ICVLoggerProvider.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; + +namespace System.Logging +{ + public interface ICVLoggerProvider : ILoggerProvider + { + void LogEntry(Log log); + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/ILogsWriter.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/ILogsWriter.cs new file mode 100644 index 0000000..1a90c32 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/ILogsWriter.cs @@ -0,0 +1,7 @@ +namespace System.Logging +{ + public interface ILogsWriter + { + void WriteLog(Log log); + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Logging/LoggerResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/Logging/LoggerResolver.cs new file mode 100644 index 0000000..b766b7c --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Logging/LoggerResolver.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Slim.Resolvers; +using System.Linq; + +namespace System.Windows.Extensions.Logging +{ + public sealed class LoggerResolver : IDependencyResolver + { + public bool CanResolve(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILogger<>) || + type == typeof(ILogger)) + { + return true; + } + + return false; + } + + public object Resolve(Slim.IServiceProvider serviceProvider, Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILogger<>)) + { + return ResolveScopedLogger(serviceProvider, type); + } + else if (type == typeof(ILogger)) + { + return ResolveLogger(serviceProvider, type); + } + else + { + throw new InvalidOperationException($"{nameof(LoggerResolver)} cannot resolve an object of type {type.Name}"); + } + } + + private static object ResolveScopedLogger(Slim.IServiceProvider serviceProvider, Type type) + { + var loggerFactory = serviceProvider.GetService(); + var categoryTypes = type.GetGenericArguments(); + var createLoggerMethod = typeof(LoggerFactoryExtensions).GetMethods().Where(m => m.IsGenericMethodDefinition && m.Name == nameof(LoggerFactoryExtensions.CreateLogger)).First(); + return createLoggerMethod.MakeGenericMethod(categoryTypes.First()).Invoke(null, new object[] { loggerFactory }); + } + + private static object ResolveLogger(Slim.IServiceProvider serviceProvider, Type type) + { + var loggerFactory = serviceProvider.GetService(); + return loggerFactory.CreateLogger(string.Empty); + } + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Models/Log.cs b/SystemExtensions.NetStandard.DependencyInjection/Models/Log.cs new file mode 100644 index 0000000..526d1f5 --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/Models/Log.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; + +namespace System.Logging +{ + public sealed record Log + { + public Exception Exception { get; set; } + public DateTime LogTime { get; set; } + public string Category { get; set; } + public string EventId { get; set; } + public LogLevel LogLevel { get; set; } + public string Message { get; set; } + public string CorrelationVector { get; set; } + } +} \ No newline at end of file diff --git a/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj b/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj new file mode 100644 index 0000000..50e88fa --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + System.DependencyInjection + LICENSE + true + latest + 1.0.0 + Alexandru Macocian + https://github.com/AlexMacocian/SystemExtensions + + + + + + True + + + + + + + + + + + diff --git a/SystemExtensions.Tests/Http/HttpClientTests.cs b/SystemExtensions.Tests/Http/HttpClientTests.cs index 35ae7eb..6b423f1 100644 --- a/SystemExtensions.Tests/Http/HttpClientTests.cs +++ b/SystemExtensions.Tests/Http/HttpClientTests.cs @@ -16,14 +16,14 @@ namespace SystemExtensionsTests.Http { private const string resourceUrl = "resource"; - private readonly Uri TestAddressUrl = new Uri("https://helloworld.xyz"); - private readonly MockHttpMessageHandler handler = new MockHttpMessageHandler(); + private static readonly Uri TestAddressUrl = new("https://helloworld.xyz"); + private readonly MockHttpMessageHandler handler = new(); private IHttpClient httpClient; [TestInitialize] public void TestInitialize() { - this.httpClient = new HttpClient(handler, false) + this.httpClient = new HttpClient(this.handler, false) { BaseAddress = TestAddressUrl }; diff --git a/SystemExtensions.Tests/SystemExtensions.Tests.csproj b/SystemExtensions.Tests/SystemExtensions.NetStandard.Tests.csproj similarity index 100% rename from SystemExtensions.Tests/SystemExtensions.Tests.csproj rename to SystemExtensions.Tests/SystemExtensions.NetStandard.Tests.csproj diff --git a/SystemExtensions.sln b/SystemExtensions.sln index 04348d6..c5a0502 100644 --- a/SystemExtensions.sln +++ b/SystemExtensions.sln @@ -5,11 +5,15 @@ VisualStudioVersion = 16.0.31005.135 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandard", "SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj", "{4CE9E55C-6016-4631-B8D4-4D763DD05F23}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemExtensions.Tests", "SystemExtensions.Tests\SystemExtensions.Tests.csproj", "{33F9C404-EEFF-4EA8-B35C-1B3EA33025A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandard.Tests", "SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj", "{33F9C404-EEFF-4EA8-B35C-1B3EA33025A6}" ProjectSection(ProjectDependencies) = postProject {4CE9E55C-6016-4631-B8D4-4D763DD05F23} = {4CE9E55C-6016-4631-B8D4-4D763DD05F23} EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandard.DependencyInjection", "SystemExtensions.NetStandard.DependencyInjection\SystemExtensions.NetStandard.DependencyInjection.csproj", "{53510F19-2E51-423A-BE79-5DE66103E7FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemExtensions.NetStandard.DependencyInjection.Tests", "SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj", "{1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,6 +28,14 @@ Global {33F9C404-EEFF-4EA8-B35C-1B3EA33025A6}.Debug|Any CPU.Build.0 = Debug|Any CPU {33F9C404-EEFF-4EA8-B35C-1B3EA33025A6}.Release|Any CPU.ActiveCfg = Release|Any CPU {33F9C404-EEFF-4EA8-B35C-1B3EA33025A6}.Release|Any CPU.Build.0 = Release|Any CPU + {53510F19-2E51-423A-BE79-5DE66103E7FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {53510F19-2E51-423A-BE79-5DE66103E7FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {53510F19-2E51-423A-BE79-5DE66103E7FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {53510F19-2E51-423A-BE79-5DE66103E7FC}.Release|Any CPU.Build.0 = Release|Any CPU + {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AFA1EEF-CEBA-4046-8466-C9B52979B7DA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE