From 1abb8e117e9b9035bc1c0a702fca7e87ce550028 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sat, 29 Jan 2022 15:16:18 +0100 Subject: [PATCH 01/11] feat: add generic parameter for AddAppFromType --- .../TypeResolver/TypeResolverTests.cs | 17 +++++++++++++++++ .../Extensions/ServiceCollectionExtension.cs | 12 ++++++++++++ .../Integration/TestRuntime.cs | 4 ++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs index aa61154b7..0683ef6cc 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs @@ -224,4 +224,21 @@ public void TestAddAppFromTypeShouldLoadSingleApp() appResolvers.Should().HaveCount(1); appResolvers.First().GetTypes().Should().BeEquivalentTo(new[] {typeof(MyAppLocalApp)}); } + + [Fact] + public void TestAddAppFromTypeGenericShouldLoadSingleApp() + { + var serviceCollection = new ServiceCollection(); + + // get apps from test project + serviceCollection.AddAppFromType(); + + serviceCollection.AddLogging(); + var provider = serviceCollection.BuildServiceProvider(); + + var appResolvers = provider.GetRequiredService>() ?? + throw new NullReferenceException("Not expected null"); + appResolvers.Should().HaveCount(1); + appResolvers.First().GetTypes().Should().BeEquivalentTo(new[] {typeof(MyAppLocalApp)}); + } } diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index 25c904951..fafb56513 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -21,6 +21,18 @@ public static IServiceCollection AddAppsFromAssembly(this IServiceCollection ser .AddAppTypeResolverIfNotExist() .AddSingleton(new AssemblyResolver(assembly)); } + + /// + /// Add a single app + /// + /// Services + /// The type of the app to add + public static IServiceCollection AddAppFromType(this IServiceCollection services) + { + return services + .AddAppModelIfNotExist() + .AddAppFromType(typeof(TAppType)); + } /// /// Add a single app diff --git a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs index f3874d3f4..4a8939c3d 100644 --- a/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs +++ b/src/Runtime/NetDaemon.Runtime.Tests/Integration/TestRuntime.cs @@ -46,7 +46,7 @@ public async Task TestApplicationReactToNewEvents() var host = hostBuilder.ConfigureServices((_, services) => services .AddSingleton(haRunner.Object) - .AddAppFromType(typeof(LocalApp)) + .AddAppFromType() .AddTransient>(_ => haRunner.ClientMock.ConnectionMock.HomeAssistantEventMock) ).Build(); @@ -84,7 +84,7 @@ public async Task TestApplicationReactToNewEventsAndThrowException() var host = hostBuilder.ConfigureServices((_, services) => services .AddSingleton(haRunner.Object) - .AddAppFromType(typeof(LocalApp)) + .AddAppFromType() ).Build(); var runnerTask = host.RunAsync(); From 691c451dba6db5e4ebcd47f7bc7ac8db1e468d09 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sat, 29 Jan 2022 15:18:30 +0100 Subject: [PATCH 02/11] feat: introduce app instance factory --- .../AppInstanceFactoryTests.cs | 67 +++++++++++++++++++ .../NetDaemon.AppModel.Tests/AppModelTests.cs | 5 +- .../Context/ApplicationScopeTests.cs | 7 +- .../Extensions/ServiceCollectionExtension.cs | 2 + .../Common/IAppInstanceFactory.cs | 6 ++ .../Internal/AppInstanceFactory.cs | 9 +++ .../Internal/Application.cs | 5 +- .../Internal/Context/ApplicationContext.cs | 4 +- 8 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs new file mode 100644 index 000000000..04940a062 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs @@ -0,0 +1,67 @@ +namespace NetDaemon.AppModel.Tests.Internal; + +public class AppInstanceFactoryTests +{ + [Fact] + public void TestCreateInstantiatesApp() + { + // ARRANGE + var provider = CreateServiceProvider(); + var factory = provider.GetRequiredService(); + + // ACT + var instance = factory.Create(provider, typeof(AppWithoutDependencies)); + + // ASSERT + Assert.NotNull(instance); + Assert.IsAssignableFrom(instance); + } + + [Fact] + public void TestCreateInstantiatesAppWithDependencies() + { + // ARRANGE + var provider = CreateServiceProvider(new AppDependency{Value = "Test Value"}); + var factory = provider.GetRequiredService(); + + // ACT + var instance = factory.Create(provider, typeof(AppWithDependencies)); + + // ASSERT + Assert.NotNull(instance); + Assert.IsAssignableFrom(instance); + Assert.Equal("Test Value", ((AppWithDependencies) instance).Dependency.Value); + } + + private IServiceProvider CreateServiceProvider(AppDependency? dependency = default) + { + var services = new ServiceCollection(); + services.AddAppModelIfNotExist(); + + if (dependency is not null) + { + services.AddSingleton(dependency); + } + + return services.BuildServiceProvider(); + } + + private class AppWithoutDependencies + { + } + + private class AppWithDependencies + { + public AppDependency Dependency { get; } + + public AppWithDependencies(AppDependency dependency) + { + Dependency = dependency; + } + } + + private class AppDependency + { + public string Value { get; init; } + } +} diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs index 18eb7923f..85c8f9641 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs @@ -138,11 +138,12 @@ public async Task TestGetApplicationsWithIdSet() public async Task TestSetStateToRunningShouldThrowException() { // ARRANGE + var factoryMock = new Mock(); var loggerMock = new Mock>(); var providerMock = new Mock(); + // ACT - var app = new Application("", typeof(object), loggerMock.Object, - providerMock.Object); + var app = new Application("", factoryMock.Object, typeof(object), loggerMock.Object, providerMock.Object); // CHECK await Assert.ThrowsAsync(() => app.SetStateAsync(ApplicationState.Running)); diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs index 4a63ae711..f10b8a77b 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs @@ -21,8 +21,11 @@ public void TestInitializedScopeReturnsOk() { var scope = new ApplicationScope { - ApplicationContext = - new ApplicationContext(typeof(object), new ServiceCollection().BuildServiceProvider()) + ApplicationContext = new ApplicationContext( + new AppInstanceFactory(), + typeof(object), + new ServiceCollection().BuildServiceProvider() + ) }; var ctx = scope.ApplicationContext; diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index fafb56513..74c7eaa90 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -97,6 +97,8 @@ internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection services .AddSingleton() .AddSingleton(s => s.GetRequiredService()) + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()) .AddTransient() .AddTransient(s => s.GetRequiredService()) .AddScopedConfigurationBinder() diff --git a/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs new file mode 100644 index 000000000..20c091dea --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs @@ -0,0 +1,6 @@ +namespace NetDaemon.AppModel; + +public interface IAppInstanceFactory +{ + object Create(IServiceProvider scopedServiceProvider, Type appType); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs new file mode 100644 index 000000000..238808a44 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs @@ -0,0 +1,9 @@ +namespace NetDaemon.AppModel.Internal; + +internal class AppInstanceFactory : IAppInstanceFactory +{ + public object Create(IServiceProvider scopedServiceProvider, Type appType) + { + return ActivatorUtilities.CreateInstance(scopedServiceProvider, appType); + } +} diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs index 4a1fadd10..dee8df039 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs @@ -3,6 +3,7 @@ namespace NetDaemon.AppModel.Internal; internal class Application : IApplication { private const int MaxTimeInInitializeAsyncInMs = 5000; + private readonly IAppInstanceFactory _appInstanceFactory; private readonly Type _applicationType; private readonly IAppStateManager? _appStateManager; private readonly ILogger _logger; @@ -12,12 +13,14 @@ internal class Application : IApplication public Application( string id, + IAppInstanceFactory appInstanceFactory, Type applicationType, ILogger logger, IServiceProvider provider ) { Id = id; + _appInstanceFactory = appInstanceFactory; _applicationType = applicationType; _logger = logger; _provider = provider; @@ -96,7 +99,7 @@ private async Task InstanceApplication() { try { - ApplicationContext = new ApplicationContext(_applicationType, _provider); + ApplicationContext = new ApplicationContext(_appInstanceFactory, _applicationType, _provider); // Init async and warn user if taking too long. var initAsyncTask = ApplicationContext.InitializeAsync(); diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs index 68c6c5366..832ad5b42 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs @@ -6,7 +6,7 @@ internal sealed class ApplicationContext private readonly IServiceScope? _serviceScope; private bool _isDisposed; - public ApplicationContext(Type appType, IServiceProvider serviceProvider) + public ApplicationContext(IAppInstanceFactory instanceFactory, Type appType, IServiceProvider serviceProvider) { // Create a new ServiceScope for all objects we create for this app // this makes sure they will all be disposed along with the app @@ -19,7 +19,7 @@ public ApplicationContext(Type appType, IServiceProvider serviceProvider) if (appScope != null) appScope.ApplicationContext = this; // Now create the actual app from the new scope - Instance = ActivatorUtilities.CreateInstance(scopedProvider, appType); + Instance = instanceFactory.Create(scopedProvider, appType); } public object Instance { get; } From 3a19c97d053738bcc2b6d04a363581fe99449018 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sat, 29 Jan 2022 15:21:57 +0100 Subject: [PATCH 03/11] style: reformat --- .../AppInstanceFactoryTests.cs | 29 ++++++++++--------- .../Common/IAppInstanceFactory.cs | 2 +- .../Internal/AppInstanceFactory.cs | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs index 04940a062..2741ea3e9 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs @@ -1,3 +1,5 @@ +using NetDaemon.AppModel.Internal; + namespace NetDaemon.AppModel.Tests.Internal; public class AppInstanceFactoryTests @@ -7,45 +9,44 @@ public void TestCreateInstantiatesApp() { // ARRANGE var provider = CreateServiceProvider(); - var factory = provider.GetRequiredService(); - + var factory = new AppInstanceFactory(); + // ACT var instance = factory.Create(provider, typeof(AppWithoutDependencies)); - + // ASSERT Assert.NotNull(instance); Assert.IsAssignableFrom(instance); } - + [Fact] public void TestCreateInstantiatesAppWithDependencies() { // ARRANGE - var provider = CreateServiceProvider(new AppDependency{Value = "Test Value"}); - var factory = provider.GetRequiredService(); - + var provider = CreateServiceProvider(new AppDependency { Value = "Test Value" }); + var factory = new AppInstanceFactory(); + // ACT var instance = factory.Create(provider, typeof(AppWithDependencies)); - + // ASSERT Assert.NotNull(instance); Assert.IsAssignableFrom(instance); - Assert.Equal("Test Value", ((AppWithDependencies) instance).Dependency.Value); + Assert.Equal("Test Value", ((AppWithDependencies)instance).Dependency.Value); } private IServiceProvider CreateServiceProvider(AppDependency? dependency = default) { var services = new ServiceCollection(); - services.AddAppModelIfNotExist(); if (dependency is not null) { services.AddSingleton(dependency); } - + return services.BuildServiceProvider(); } - + private class AppWithoutDependencies { } @@ -57,11 +58,11 @@ private class AppWithDependencies public AppWithDependencies(AppDependency dependency) { Dependency = dependency; - } + } } private class AppDependency { public string Value { get; init; } } -} +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs index 20c091dea..35366ff5f 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs @@ -3,4 +3,4 @@ public interface IAppInstanceFactory { object Create(IServiceProvider scopedServiceProvider, Type appType); -} \ No newline at end of file +} diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs index 238808a44..5a096eaaa 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs @@ -6,4 +6,4 @@ public object Create(IServiceProvider scopedServiceProvider, Type appType) { return ActivatorUtilities.CreateInstance(scopedServiceProvider, appType); } -} +} \ No newline at end of file From 8fa05a57d966e96364935a3b3279bc102f8679a8 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sat, 29 Jan 2022 15:26:53 +0100 Subject: [PATCH 04/11] fix: use nullable property --- .../NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs index 2741ea3e9..680374181 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs @@ -63,6 +63,6 @@ public AppWithDependencies(AppDependency dependency) private class AppDependency { - public string Value { get; init; } + public string? Value { get; init; } } } \ No newline at end of file From 72383f4df596ff164e26877936dbbf444e994ada Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sat, 29 Jan 2022 18:32:49 +0100 Subject: [PATCH 05/11] feat: introduce app instances --- .../AppInstanceFactoryTests.cs | 68 ------------------- .../NetDaemon.AppModel.Tests/AppModelTests.cs | 9 +-- .../Context/ApplicationScopeTests.cs | 6 +- .../Extensions/ServiceCollectionExtension.cs | 42 ++++++++---- .../Common/IAppInstanceFactory.cs | 6 -- .../Internal/AppInstanceFactory.cs | 9 --- .../Internal/AppModelContext.cs | 44 ++++-------- .../Internal/Application.cs | 37 +++++----- .../Internal/Context/ApplicationContext.cs | 6 +- .../Internal/Instance/AppInstanceHelper.cs | 28 ++++++++ .../Internal/Instance/DynamicAppInstance.cs | 27 ++++++++ .../Instance/DynamicAppInstanceResolver.cs | 19 ++++++ .../Internal/Instance/FactoryAppInstance.cs | 23 +++++++ .../Instance/FactoryAppInstanceResolver.cs | 16 +++++ .../Internal/Instance/IAppInstance.cs | 10 +++ .../Internal/Instance/IAppInstanceResolver.cs | 6 ++ .../Internal/TypeResolver/AppTypeResolver.cs | 18 ++++- ...ppResolver.cs => SingleAppTypeResolver.cs} | 4 +- 18 files changed, 219 insertions(+), 159 deletions(-) delete mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs rename src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/{SingleAppResolver.cs => SingleAppTypeResolver.cs} (69%) diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs deleted file mode 100644 index 680374181..000000000 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppInstanceFactoryTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using NetDaemon.AppModel.Internal; - -namespace NetDaemon.AppModel.Tests.Internal; - -public class AppInstanceFactoryTests -{ - [Fact] - public void TestCreateInstantiatesApp() - { - // ARRANGE - var provider = CreateServiceProvider(); - var factory = new AppInstanceFactory(); - - // ACT - var instance = factory.Create(provider, typeof(AppWithoutDependencies)); - - // ASSERT - Assert.NotNull(instance); - Assert.IsAssignableFrom(instance); - } - - [Fact] - public void TestCreateInstantiatesAppWithDependencies() - { - // ARRANGE - var provider = CreateServiceProvider(new AppDependency { Value = "Test Value" }); - var factory = new AppInstanceFactory(); - - // ACT - var instance = factory.Create(provider, typeof(AppWithDependencies)); - - // ASSERT - Assert.NotNull(instance); - Assert.IsAssignableFrom(instance); - Assert.Equal("Test Value", ((AppWithDependencies)instance).Dependency.Value); - } - - private IServiceProvider CreateServiceProvider(AppDependency? dependency = default) - { - var services = new ServiceCollection(); - - if (dependency is not null) - { - services.AddSingleton(dependency); - } - - return services.BuildServiceProvider(); - } - - private class AppWithoutDependencies - { - } - - private class AppWithDependencies - { - public AppDependency Dependency { get; } - - public AppWithDependencies(AppDependency dependency) - { - Dependency = dependency; - } - } - - private class AppDependency - { - public string? Value { get; init; } - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs index 85c8f9641..229d8af77 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NetDaemon.AppModel.Internal; +using NetDaemon.AppModel.Internal.Resolver; using NetDaemon.AppModel.Tests.Helpers; namespace NetDaemon.AppModel.Tests.Internal; @@ -138,12 +139,12 @@ public async Task TestGetApplicationsWithIdSet() public async Task TestSetStateToRunningShouldThrowException() { // ARRANGE - var factoryMock = new Mock(); - var loggerMock = new Mock>(); - var providerMock = new Mock(); + var provider = Mock.Of(); + var logger = Mock.Of>(); + var instance = Mock.Of(); // ACT - var app = new Application("", factoryMock.Object, typeof(object), loggerMock.Object, providerMock.Object); + var app = new Application(provider, logger, instance); // CHECK await Assert.ThrowsAsync(() => app.SetStateAsync(ApplicationState.Running)); diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs index f10b8a77b..a531f4f23 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs @@ -1,4 +1,5 @@ using NetDaemon.AppModel.Internal; +using NetDaemon.AppModel.Internal.Resolver; namespace NetDaemon.AppModel.Tests.Context; @@ -22,9 +23,8 @@ public void TestInitializedScopeReturnsOk() var scope = new ApplicationScope { ApplicationContext = new ApplicationContext( - new AppInstanceFactory(), - typeof(object), - new ServiceCollection().BuildServiceProvider() + new ServiceCollection().BuildServiceProvider(), + Mock.Of() ) }; var ctx = scope.ApplicationContext; diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index 74c7eaa90..c0090ca2e 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -1,6 +1,7 @@ using System.Reflection; using NetDaemon.AppModel.Internal.Compiler; using NetDaemon.AppModel.Internal.Config; +using NetDaemon.AppModel.Internal.Resolver; namespace NetDaemon.AppModel; @@ -9,6 +10,15 @@ namespace NetDaemon.AppModel; /// public static class ServiceCollectionExtensions { + public static IServiceCollection AddApp( + this IServiceCollection services, + Func factoryFunc) where TAppType : class + { + return services + .AddAppModelIfNotExist() + .AddSingleton(new FactoryAppInstanceResolver(factoryFunc)); + } + /// /// Adds applications from the specified assembly /// @@ -43,7 +53,7 @@ public static IServiceCollection AddAppFromType(this IServiceCollection services { return services .AddAppModelIfNotExist() - .AddSingleton(new SingleAppResolver(type)); + .AddSingleton(new SingleAppTypeResolver(type)); } /// @@ -63,12 +73,13 @@ public static IServiceCollection AddAppsFromSource(this IServiceCollection servi .AddOptions().Configure(settings => settings.UseDebug = useDebug); // We need to compile it here so we can dynamically add the service providers - var assemblyResolver = - ActivatorUtilities.CreateInstance(services.BuildServiceProvider()); + var assemblyResolver = ActivatorUtilities.CreateInstance(services.BuildServiceProvider()); services.RegisterDynamicFunctions(assemblyResolver.GetResolvedAssembly()); - // And not register the assembly resolver that will have the assembly already compiled + + // And now register the assembly resolver that will have the assembly already compiled services.AddSingleton(assemblyResolver); services.AddSingleton(s => s.GetRequiredService()); + return services; } @@ -97,8 +108,6 @@ internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection services .AddSingleton() .AddSingleton(s => s.GetRequiredService()) - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()) .AddTransient() .AddTransient(s => s.GetRequiredService()) .AddScopedConfigurationBinder() @@ -109,13 +118,22 @@ internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection internal static IServiceCollection AddAppTypeResolverIfNotExist(this IServiceCollection services) { - // Check if we already registered - if (services.Any(n => n.ImplementationType == typeof(AppTypeResolver))) - return services; + // Check if already registered + if (services.All(descriptor => descriptor.ImplementationType != typeof(AppTypeResolver))) + { + services + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()); + } + + // Check if already registered + if (services.All(n => n.ImplementationType != typeof(DynamicAppInstanceResolver))) + { + services + .AddSingleton() + .AddSingleton(s => s.GetRequiredService()); + } - services - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()); return services; } diff --git a/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs deleted file mode 100644 index 35366ff5f..000000000 --- a/src/AppModel/NetDaemon.AppModel/Common/IAppInstanceFactory.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NetDaemon.AppModel; - -public interface IAppInstanceFactory -{ - object Create(IServiceProvider scopedServiceProvider, Type appType); -} diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs deleted file mode 100644 index 5a096eaaa..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppInstanceFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NetDaemon.AppModel.Internal; - -internal class AppInstanceFactory : IAppInstanceFactory -{ - public object Create(IServiceProvider scopedServiceProvider, Type appType) - { - return ActivatorUtilities.CreateInstance(scopedServiceProvider, appType); - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index 2430f2497..a89f306bd 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using NetDaemon.AppModel.Internal.Resolver; namespace NetDaemon.AppModel.Internal; @@ -6,16 +6,14 @@ internal class AppModelContext : IAppModelContext, IAsyncInitializable { private readonly List _applications = new(); - private readonly IEnumerable _appTypeResolvers; + private readonly IEnumerable _appInstanceResolvers; private readonly IServiceProvider _provider; private bool _isDisposed; - public AppModelContext( - IEnumerable appTypeResolvers, - IServiceProvider provider) + public AppModelContext(IEnumerable appInstanceResolvers, IServiceProvider provider) { + _appInstanceResolvers = appInstanceResolvers; _provider = provider; - _appTypeResolvers = appTypeResolvers; } public IReadOnlyCollection Applications => _applications; @@ -37,41 +35,27 @@ public async Task InitializeAsync(CancellationToken cancellationToken) private async Task LoadApplications() { - var applicationTypes = GetNetDaemonApplicationTypes().ToArray(); - var loadOnlyFocusedApps = ShouldLoadOnlyFocusedApps(applicationTypes); - foreach (var appType in applicationTypes) + var appInstances = GetAppInstances().ToList(); + var loadOnlyFocusedApps = ShouldLoadOnlyFocusedApps(appInstances); + + foreach (var appInstance in appInstances) { - if (loadOnlyFocusedApps && !HasFocusAttribute(appType)) + if (loadOnlyFocusedApps && !appInstance.HasFocus) continue; // We do not load applications that does not have focus attr and we are in focus mode - var appAttribute = appType.GetCustomAttribute(); - var id = appAttribute?.Id ?? appType.FullName ?? - throw new InvalidOperationException("Type was not expected to be null"); - - var app = ActivatorUtilities.CreateInstance(_provider, id, appType); + var app = ActivatorUtilities.CreateInstance(_provider, appInstance); await app.InitializeAsync().ConfigureAwait(false); _applications.Add(app); } } - private IEnumerable GetNetDaemonApplicationTypes() - { - // Get all classes with the [NetDaemonAppAttribute] - return _appTypeResolvers.SelectMany(r => r.GetTypes()) - .Where(n => n.IsClass && - !n.IsGenericType && - !n.IsAbstract && - n.GetCustomAttribute() != null - ); - } - - private static bool ShouldLoadOnlyFocusedApps(IEnumerable types) + private IEnumerable GetAppInstances() { - return types.Any(n => n.GetCustomAttribute() is not null); + return _appInstanceResolvers.SelectMany(resolver => resolver.GetApps()); } - private static bool HasFocusAttribute(Type type) + private static bool ShouldLoadOnlyFocusedApps(IEnumerable types) { - return type.GetCustomAttribute() is not null; + return types.Any(instance => instance.HasFocus); } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs index dee8df039..2bcddff52 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs @@ -1,29 +1,24 @@ +using NetDaemon.AppModel.Internal.Resolver; + namespace NetDaemon.AppModel.Internal; internal class Application : IApplication { private const int MaxTimeInInitializeAsyncInMs = 5000; - private readonly IAppInstanceFactory _appInstanceFactory; - private readonly Type _applicationType; - private readonly IAppStateManager? _appStateManager; - private readonly ILogger _logger; + private readonly IServiceProvider _provider; + private readonly ILogger _logger; + private readonly IAppStateManager? _appStateManager; + private readonly IAppInstance _instance; private bool _isErrorState; - public Application( - string id, - IAppInstanceFactory appInstanceFactory, - Type applicationType, - ILogger logger, - IServiceProvider provider - ) + public Application(IServiceProvider provider, ILogger logger, IAppInstance instance) { - Id = id; - _appInstanceFactory = appInstanceFactory; - _applicationType = applicationType; - _logger = logger; _provider = provider; + _logger = logger; + _instance = instance; + // Can be missing so it is not injected in the constructor _appStateManager = provider.GetService(); } @@ -31,7 +26,7 @@ IServiceProvider provider // Used in tests internal ApplicationContext? ApplicationContext { get; private set; } - public string Id { get; } + public string Id => _instance.Id; public ApplicationState State { @@ -74,7 +69,7 @@ private async Task UnloadApplication(ApplicationState state) { await ApplicationContext.DisposeAsync().ConfigureAwait(false); ApplicationContext = null; - _logger.LogInformation("Successfully unloaded app {id}", Id); + _logger.LogInformation("Successfully unloaded app {Id}", Id); await SaveStateIfStateManagerExistAsync(state).ConfigureAwait(false); } } @@ -99,7 +94,7 @@ private async Task InstanceApplication() { try { - ApplicationContext = new ApplicationContext(_appInstanceFactory, _applicationType, _provider); + ApplicationContext = new ApplicationContext(_provider, _instance); // Init async and warn user if taking too long. var initAsyncTask = ApplicationContext.InitializeAsync(); @@ -107,18 +102,18 @@ private async Task InstanceApplication() await Task.WhenAny(initAsyncTask, timeoutTask).ConfigureAwait(false); if (timeoutTask.IsCompleted) _logger.LogWarning( - "InitializeAsync is taking too long to execute in application {app}, this function should not be blocking", + "InitializeAsync is taking too long to execute in application {Id}, this function should not be blocking", Id); if (!initAsyncTask.IsCompleted) await initAsyncTask; // Continue to wait even if timeout is set so we do not miss errors await SaveStateIfStateManagerExistAsync(ApplicationState.Running); - _logger.LogInformation("Successfully loaded app {id}", Id); + _logger.LogInformation("Successfully loaded app {Id}", Id); } catch (Exception e) { - _logger.LogError(e, "Error loading app {id}", Id); + _logger.LogError(e, "Error loading app {Id}", Id); await SetStateAsync(ApplicationState.Error); } } diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs index 832ad5b42..838df9a57 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs @@ -1,3 +1,5 @@ +using NetDaemon.AppModel.Internal.Resolver; + namespace NetDaemon.AppModel.Internal; internal sealed class ApplicationContext @@ -6,7 +8,7 @@ internal sealed class ApplicationContext private readonly IServiceScope? _serviceScope; private bool _isDisposed; - public ApplicationContext(IAppInstanceFactory instanceFactory, Type appType, IServiceProvider serviceProvider) + public ApplicationContext(IServiceProvider serviceProvider, IAppInstance instance) { // Create a new ServiceScope for all objects we create for this app // this makes sure they will all be disposed along with the app @@ -19,7 +21,7 @@ public ApplicationContext(IAppInstanceFactory instanceFactory, Type appType, ISe if (appScope != null) appScope.ApplicationContext = this; // Now create the actual app from the new scope - Instance = instanceFactory.Create(scopedProvider, appType); + Instance = instance.Create(scopedProvider); } public object Instance { get; } diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs new file mode 100644 index 000000000..bcaf3687d --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs @@ -0,0 +1,28 @@ +using System.Reflection; + +namespace NetDaemon.AppModel.Internal.Resolver; + +internal static class AppInstanceHelper +{ + public static string GetAppId(Type type) + { + var attribute = type.GetCustomAttribute(); + if (attribute is null) + { + throw new InvalidOperationException($"{type} has no {nameof(NetDaemonAppAttribute)}"); + } + + var id = attribute.Id ?? type.FullName; + if (string.IsNullOrEmpty(id)) + { + throw new InvalidOperationException($"Could not get app id from {type}"); + } + + return id; + } + + public static bool GetAppFocus(Type type) + { + return type.GetCustomAttribute() is not null; + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs new file mode 100644 index 000000000..da3c81103 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs @@ -0,0 +1,27 @@ +using System.Reflection; + +namespace NetDaemon.AppModel.Internal.Resolver; + +internal class DynamicAppInstance : IAppInstance +{ + private readonly Type _type; + + public DynamicAppInstance(Type type) + { + _type = type; + + Id = AppInstanceHelper.GetAppId(type); + HasFocus = AppInstanceHelper.GetAppFocus(type); + } + + public string Id { get; } + + public bool HasFocus { get; } + + public object Create(IServiceProvider scopedServiceProvider) + { + return ActivatorUtilities.CreateInstance(scopedServiceProvider, _type); + } + + +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs new file mode 100644 index 000000000..3fc7a0313 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs @@ -0,0 +1,19 @@ +namespace NetDaemon.AppModel.Internal.Resolver; + +internal class DynamicAppInstanceResolver : IAppInstanceResolver +{ + private readonly IEnumerable _resolvers; + + public DynamicAppInstanceResolver(IEnumerable resolvers) + { + _resolvers = resolvers; + } + + public IReadOnlyCollection GetApps() + { + return _resolvers + .SelectMany(resolver => resolver.GetTypes()) + .Select(type => new DynamicAppInstance(type)) + .ToList(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs new file mode 100644 index 000000000..f2427e67e --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs @@ -0,0 +1,23 @@ +namespace NetDaemon.AppModel.Internal.Resolver; + +internal class FactoryAppInstance : IAppInstance where TAppType : class +{ + private readonly Func _factoryFunc; + + public FactoryAppInstance(Func factoryFunc) + { + _factoryFunc = factoryFunc; + + Id = AppInstanceHelper.GetAppId(typeof(TAppType)); + HasFocus = AppInstanceHelper.GetAppFocus(typeof(TAppType)); + } + + public object Create(IServiceProvider scopedServiceProvider) + { + return _factoryFunc.Invoke(scopedServiceProvider); + } + + public string Id { get; } + + public bool HasFocus { get; } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs new file mode 100644 index 000000000..f1c41116f --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs @@ -0,0 +1,16 @@ +namespace NetDaemon.AppModel.Internal.Resolver; + +internal class FactoryAppInstanceResolver : IAppInstanceResolver where TAppType : class +{ + private readonly Func _factoryFunc; + + public FactoryAppInstanceResolver(Func factoryFunc) + { + _factoryFunc = factoryFunc; + } + + public IReadOnlyCollection GetApps() + { + return new[] { new FactoryAppInstance(_factoryFunc) }; + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs new file mode 100644 index 000000000..f690aec57 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs @@ -0,0 +1,10 @@ +namespace NetDaemon.AppModel.Internal.Resolver; + +internal interface IAppInstance +{ + string Id { get; } + + bool HasFocus { get; } + + object Create(IServiceProvider scopedServiceProvider); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs new file mode 100644 index 000000000..e4cba6f52 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs @@ -0,0 +1,6 @@ +namespace NetDaemon.AppModel.Internal.Resolver; + +internal interface IAppInstanceResolver +{ + IReadOnlyCollection GetApps(); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs index d32db3b4c..ce5ab760c 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs @@ -1,3 +1,5 @@ +using System.Reflection; + namespace NetDaemon.AppModel.Internal.TypeResolver; internal class AppTypeResolver : IAppTypeResolver @@ -12,7 +14,19 @@ public AppTypeResolver(IEnumerable assemblyResolvers) public IReadOnlyCollection GetTypes() { return _assemblyResolvers - .Select(n => n.GetResolvedAssembly()) - .SelectMany(s => s.GetTypes()).ToList(); + .Select(resolver => resolver.GetResolvedAssembly()) + .SelectMany(assembly => assembly.GetTypes()) + .Where(IsNetDaemonAppType) + .ToList(); + } + + private static bool IsNetDaemonAppType(Type type) + { + if (!type.IsClass || !type.IsGenericType || !type.IsAbstract) + { + return false; + } + + return type.GetCustomAttribute() is not null; } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs similarity index 69% rename from src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppResolver.cs rename to src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs index 5667291e0..e623679a7 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppResolver.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs @@ -1,10 +1,10 @@ namespace NetDaemon.AppModel.Internal.TypeResolver; -internal class SingleAppResolver : IAppTypeResolver +internal class SingleAppTypeResolver : IAppTypeResolver { private readonly Type _appType; - public SingleAppResolver(Type appType) + public SingleAppTypeResolver(Type appType) { _appType = appType; } From 880250454a32b8756e44c7cbd9d0176d2cdb4afb Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 14:04:27 +0100 Subject: [PATCH 06/11] refactor: modify names and layers based on discussions --- .../Extensions/ServiceCollectionExtension.cs | 53 ++++++++++--------- .../NetDaemon.AppModel/GlobalUsings.cs | 3 -- ...DynamicallyCompiledAppAssemblyProvider.cs} | 35 +++--------- .../IAppAssemblyProvider.cs | 8 +++ .../SingleAppAssemblyProvider.cs | 18 +++++++ .../AppFactoryHelper.cs} | 10 ++-- .../Internal/AppFactory/FuncAppFactory.cs | 32 +++++++++++ .../Internal/AppFactory/IAppFactory.cs | 10 ++++ .../Internal/AppFactory/TypeAppFactory.cs | 18 +++++++ .../AssemblyAppFactoryProvider.cs | 35 ++++++++++++ .../AppFactoryProvider/IAppFactoryProvider.cs | 8 +++ .../SingleAppFactoryProvider.cs | 18 +++++++ .../Internal/AppModelContext.cs | 27 +++++----- .../Internal/Application.cs | 12 ++--- .../AssemblyResolver/IAssemblyResolver.cs | 8 --- .../Internal/Context/ApplicationContext.cs | 6 +-- .../Internal/Instance/DynamicAppInstance.cs | 27 ---------- .../Instance/DynamicAppInstanceResolver.cs | 19 ------- .../Internal/Instance/FactoryAppInstance.cs | 23 -------- .../Instance/FactoryAppInstanceResolver.cs | 16 ------ .../Internal/Instance/IAppInstance.cs | 10 ---- .../Internal/Instance/IAppInstanceResolver.cs | 6 --- .../Internal/TypeResolver/AppTypeResolver.cs | 32 ----------- .../Internal/TypeResolver/IAppTypeResolver.cs | 13 ----- .../TypeResolver/SingleAppTypeResolver.cs | 16 ------ 25 files changed, 209 insertions(+), 254 deletions(-) rename src/AppModel/NetDaemon.AppModel/Internal/{AssemblyResolver/AssemblyResolver.cs => AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs} (53%) create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs rename src/AppModel/NetDaemon.AppModel/Internal/{Instance/AppInstanceHelper.cs => AppFactory/AppFactoryHelper.cs} (61%) create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/IAssemblyResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/IAppTypeResolver.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index c0090ca2e..c77ea3354 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -1,7 +1,9 @@ using System.Reflection; +using NetDaemon.AppModel.Internal.AppAssemblyProvider; +using NetDaemon.AppModel.Internal.AppFactory; +using NetDaemon.AppModel.Internal.AppFactoryProvider; using NetDaemon.AppModel.Internal.Compiler; using NetDaemon.AppModel.Internal.Config; -using NetDaemon.AppModel.Internal.Resolver; namespace NetDaemon.AppModel; @@ -10,13 +12,25 @@ namespace NetDaemon.AppModel; /// public static class ServiceCollectionExtensions { + /// + /// Adds a single app that gets instantiated by the provided Func + /// + /// Services + /// The Func used to create the app + /// The id of the app. This parameter is optional, + /// by default it tries to locate the id from the specified TAppType. + /// Whether this app has focus or not. This parameter is optional, + /// by default it tries to check this using the FocusAttribute + /// The type of the app public static IServiceCollection AddApp( this IServiceCollection services, - Func factoryFunc) where TAppType : class + Func factoryFunc, + string? id = default, + bool? focus = default) where TAppType : class { return services .AddAppModelIfNotExist() - .AddSingleton(new FactoryAppInstanceResolver(factoryFunc)); + .AddSingleton(new FuncAppFactory(factoryFunc, id, focus)); } /// @@ -29,9 +43,9 @@ public static IServiceCollection AddAppsFromAssembly(this IServiceCollection ser return services .AddAppModelIfNotExist() .AddAppTypeResolverIfNotExist() - .AddSingleton(new AssemblyResolver(assembly)); + .AddSingleton(new SingleAppAssemblyProvider(assembly)); } - + /// /// Add a single app /// @@ -53,7 +67,7 @@ public static IServiceCollection AddAppFromType(this IServiceCollection services { return services .AddAppModelIfNotExist() - .AddSingleton(new SingleAppTypeResolver(type)); + .AddSingleton(new SingleAppFactoryProvider(type)); } /// @@ -73,12 +87,12 @@ public static IServiceCollection AddAppsFromSource(this IServiceCollection servi .AddOptions().Configure(settings => settings.UseDebug = useDebug); // We need to compile it here so we can dynamically add the service providers - var assemblyResolver = ActivatorUtilities.CreateInstance(services.BuildServiceProvider()); - services.RegisterDynamicFunctions(assemblyResolver.GetResolvedAssembly()); + var assemblyResolver = ActivatorUtilities.CreateInstance(services.BuildServiceProvider()); + services.RegisterDynamicFunctions(assemblyResolver.GetAppAssembly()); // And now register the assembly resolver that will have the assembly already compiled services.AddSingleton(assemblyResolver); - services.AddSingleton(s => s.GetRequiredService()); + services.AddSingleton(s => s.GetRequiredService()); return services; } @@ -118,23 +132,12 @@ internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection internal static IServiceCollection AddAppTypeResolverIfNotExist(this IServiceCollection services) { - // Check if already registered - if (services.All(descriptor => descriptor.ImplementationType != typeof(AppTypeResolver))) - { - services - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()); - } - - // Check if already registered - if (services.All(n => n.ImplementationType != typeof(DynamicAppInstanceResolver))) - { - services - .AddSingleton() - .AddSingleton(s => s.GetRequiredService()); - } + if (services.Any(descriptor => descriptor.ImplementationType == typeof(AssemblyAppFactoryProvider))) + return services; - return services; + return services + .AddSingleton() + .AddSingleton(provider => provider.GetRequiredService()); } private static IServiceCollection AddScopedConfigurationBinder(this IServiceCollection services) diff --git a/src/AppModel/NetDaemon.AppModel/GlobalUsings.cs b/src/AppModel/NetDaemon.AppModel/GlobalUsings.cs index aae6ba231..4107aa59c 100644 --- a/src/AppModel/NetDaemon.AppModel/GlobalUsings.cs +++ b/src/AppModel/NetDaemon.AppModel/GlobalUsings.cs @@ -7,10 +7,7 @@ global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; -global using NetDaemon.AppModel.Common; global using NetDaemon.AppModel.Internal; -global using NetDaemon.AppModel.Common.TypeResolver; -global using NetDaemon.AppModel.Internal.TypeResolver; // Make the internal visible to test project [assembly: InternalsVisibleTo("NetDaemon.AppModel.Tests")] diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/AssemblyResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs similarity index 53% rename from src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/AssemblyResolver.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs index c6a90f110..be39a3586 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/AssemblyResolver.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs @@ -1,42 +1,23 @@ -using System.Reflection; +using System.Reflection; using NetDaemon.AppModel.Internal.Compiler; -namespace NetDaemon.AppModel.Internal; +namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; -internal class AssemblyResolver : IAssemblyResolver -{ - private readonly Assembly _assembly; - - public AssemblyResolver( - Assembly assembly - ) - { - _assembly = assembly; - } - - public Assembly GetResolvedAssembly() - { - return _assembly; - } -} - -internal class DynamicallyCompiledAssemblyResolver : IAssemblyResolver, IDisposable +internal class DynamicallyCompiledAppAssemblyProvider : IAppAssemblyProvider, IDisposable { private readonly ICompiler _compiler; + private Assembly? _compiledAssembly; private CollectibleAssemblyLoadContext? _currentContext; - public DynamicallyCompiledAssemblyResolver( - ICompiler compiler - ) + public DynamicallyCompiledAppAssemblyProvider(ICompiler compiler) { _compiler = compiler; } - public Assembly GetResolvedAssembly() + public Assembly GetAppAssembly() { - // We reuse an already compiled assembly since we only - // compile once per start + // We reuse an already compiled assembly since we only compile once per start if (_compiledAssembly is not null) return _compiledAssembly; @@ -54,4 +35,4 @@ public void Dispose() GC.Collect(); GC.WaitForPendingFinalizers(); } -} +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs new file mode 100644 index 000000000..621b27b95 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; + +internal interface IAppAssemblyProvider +{ + public Assembly GetAppAssembly(); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs new file mode 100644 index 000000000..04caa8e2c --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs @@ -0,0 +1,18 @@ +using System.Reflection; + +namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; + +internal class SingleAppAssemblyProvider : IAppAssemblyProvider +{ + private readonly Assembly _assembly; + + public SingleAppAssemblyProvider(Assembly assembly) + { + _assembly = assembly; + } + + public Assembly GetAppAssembly() + { + return _assembly; + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs similarity index 61% rename from src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs index bcaf3687d..f113d7e8d 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/AppInstanceHelper.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs @@ -1,18 +1,14 @@ using System.Reflection; -namespace NetDaemon.AppModel.Internal.Resolver; +namespace NetDaemon.AppModel.Internal.AppFactory; -internal static class AppInstanceHelper +internal static class AppFactoryHelper { public static string GetAppId(Type type) { var attribute = type.GetCustomAttribute(); - if (attribute is null) - { - throw new InvalidOperationException($"{type} has no {nameof(NetDaemonAppAttribute)}"); - } + var id = attribute?.Id ?? type.FullName; - var id = attribute.Id ?? type.FullName; if (string.IsNullOrEmpty(id)) { throw new InvalidOperationException($"Could not get app id from {type}"); diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs new file mode 100644 index 000000000..cfc6d6c17 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs @@ -0,0 +1,32 @@ +namespace NetDaemon.AppModel.Internal.AppFactory; + +internal class FuncAppFactory : FuncAppFactory where TAppType : class +{ + public FuncAppFactory(Func factoryFunc, string? id = default, bool? focus = default) : + base( + factoryFunc, + id ?? AppFactoryHelper.GetAppId(typeof(TAppType)), + focus ?? AppFactoryHelper.GetAppFocus(typeof(TAppType)) + ) + { + } +} + +internal class FuncAppFactory : IAppFactory +{ + private readonly Func _factoryFunc; + + public FuncAppFactory(Func factoryFunc, string id, bool focus) + { + _factoryFunc = factoryFunc; + + Id = id; + HasFocus = focus; + } + + public object Create(IServiceProvider provider) => _factoryFunc.Invoke(provider); + + public string Id { get; } + + public bool HasFocus { get; } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs new file mode 100644 index 000000000..8b3bc71c4 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs @@ -0,0 +1,10 @@ +namespace NetDaemon.AppModel.Internal.AppFactory; + +internal interface IAppFactory +{ + object Create(IServiceProvider provider); + + string Id { get; } + + bool HasFocus { get; } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs new file mode 100644 index 000000000..0dab2910c --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs @@ -0,0 +1,18 @@ +namespace NetDaemon.AppModel.Internal.AppFactory; + +internal class TypeAppFactory : FuncAppFactory +{ + public TypeAppFactory(Type type, string? id = default, bool? focus = default) : + base( + CreateFactoryFunc(type), + id ?? AppFactoryHelper.GetAppId(type), + focus ?? AppFactoryHelper.GetAppFocus(type) + ) + { + } + + private static Func CreateFactoryFunc(Type type) + { + return provider => ActivatorUtilities.CreateInstance(provider, type); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs new file mode 100644 index 000000000..788000075 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using NetDaemon.AppModel.Internal.AppAssemblyProvider; +using NetDaemon.AppModel.Internal.AppFactory; + +namespace NetDaemon.AppModel.Internal.AppFactoryProvider; + +internal class AssemblyAppFactoryProvider : IAppFactoryProvider +{ + private readonly IEnumerable _assemblyResolvers; + + public AssemblyAppFactoryProvider(IEnumerable assemblyResolvers) + { + _assemblyResolvers = assemblyResolvers; + } + + public IReadOnlyCollection GetAppFactories() + { + return _assemblyResolvers + .Select(resolver => resolver.GetAppAssembly()) + .SelectMany(assembly => assembly.GetTypes()) + .Where(IsNetDaemonAppType) + .Select(type => new TypeAppFactory(type)) + .ToList(); + } + + private static bool IsNetDaemonAppType(Type type) + { + if (!type.IsClass || !type.IsGenericType || !type.IsAbstract) + { + return false; + } + + return type.GetCustomAttribute() is not null; + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs new file mode 100644 index 000000000..cac39c46f --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs @@ -0,0 +1,8 @@ +using NetDaemon.AppModel.Internal.AppFactory; + +namespace NetDaemon.AppModel.Internal.AppFactoryProvider; + +internal interface IAppFactoryProvider +{ + IReadOnlyCollection GetAppFactories(); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs new file mode 100644 index 000000000..e2b250e0f --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs @@ -0,0 +1,18 @@ +using NetDaemon.AppModel.Internal.AppFactory; + +namespace NetDaemon.AppModel.Internal.AppFactoryProvider; + +internal class SingleAppFactoryProvider : IAppFactoryProvider +{ + private readonly Type _appType; + + public SingleAppFactoryProvider(Type appType) + { + _appType = appType; + } + + public IReadOnlyCollection GetAppFactories() + { + return new[] { new TypeAppFactory(_appType) }; + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index a89f306bd..3b3c6999a 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -1,4 +1,5 @@ -using NetDaemon.AppModel.Internal.Resolver; +using NetDaemon.AppModel.Internal.AppFactoryProvider; +using IAppFactory = NetDaemon.AppModel.Internal.AppFactory.IAppFactory; namespace NetDaemon.AppModel.Internal; @@ -6,13 +7,13 @@ internal class AppModelContext : IAppModelContext, IAsyncInitializable { private readonly List _applications = new(); - private readonly IEnumerable _appInstanceResolvers; + private readonly IEnumerable _appFactoryProviders; private readonly IServiceProvider _provider; private bool _isDisposed; - public AppModelContext(IEnumerable appInstanceResolvers, IServiceProvider provider) + public AppModelContext(IEnumerable appFactoryProviders, IServiceProvider provider) { - _appInstanceResolvers = appInstanceResolvers; + _appFactoryProviders = appFactoryProviders; _provider = provider; } @@ -35,27 +36,27 @@ public async Task InitializeAsync(CancellationToken cancellationToken) private async Task LoadApplications() { - var appInstances = GetAppInstances().ToList(); - var loadOnlyFocusedApps = ShouldLoadOnlyFocusedApps(appInstances); + var factories = GetAppFactories().ToList(); + var loadOnlyFocusedApps = ShouldLoadOnlyFocusedApps(factories); - foreach (var appInstance in appInstances) + foreach (var factory in factories) { - if (loadOnlyFocusedApps && !appInstance.HasFocus) + if (loadOnlyFocusedApps && !factory.HasFocus) continue; // We do not load applications that does not have focus attr and we are in focus mode - var app = ActivatorUtilities.CreateInstance(_provider, appInstance); + var app = ActivatorUtilities.CreateInstance(_provider, factory); await app.InitializeAsync().ConfigureAwait(false); _applications.Add(app); } } - private IEnumerable GetAppInstances() + private IEnumerable GetAppFactories() { - return _appInstanceResolvers.SelectMany(resolver => resolver.GetApps()); + return _appFactoryProviders.SelectMany(provider => provider.GetAppFactories()); } - private static bool ShouldLoadOnlyFocusedApps(IEnumerable types) + private static bool ShouldLoadOnlyFocusedApps(IEnumerable factories) { - return types.Any(instance => instance.HasFocus); + return factories.Any(factory => factory.HasFocus); } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs index 2bcddff52..e35b54e06 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs @@ -1,4 +1,4 @@ -using NetDaemon.AppModel.Internal.Resolver; +using NetDaemon.AppModel.Internal.AppFactory; namespace NetDaemon.AppModel.Internal; @@ -8,16 +8,16 @@ internal class Application : IApplication private readonly IServiceProvider _provider; private readonly ILogger _logger; + private readonly IAppFactory _appFactory; private readonly IAppStateManager? _appStateManager; - private readonly IAppInstance _instance; private bool _isErrorState; - public Application(IServiceProvider provider, ILogger logger, IAppInstance instance) + public Application(IServiceProvider provider, ILogger logger, IAppFactory appFactory) { _provider = provider; _logger = logger; - _instance = instance; + _appFactory = appFactory; // Can be missing so it is not injected in the constructor _appStateManager = provider.GetService(); @@ -26,7 +26,7 @@ public Application(IServiceProvider provider, ILogger logger, IAppI // Used in tests internal ApplicationContext? ApplicationContext { get; private set; } - public string Id => _instance.Id; + public string Id => _appFactory.Id; public ApplicationState State { @@ -94,7 +94,7 @@ private async Task InstanceApplication() { try { - ApplicationContext = new ApplicationContext(_provider, _instance); + ApplicationContext = new ApplicationContext(_provider, _appFactory); // Init async and warn user if taking too long. var initAsyncTask = ApplicationContext.InitializeAsync(); diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/IAssemblyResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/IAssemblyResolver.cs deleted file mode 100644 index ed6b03e4e..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/AssemblyResolver/IAssemblyResolver.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; - -namespace NetDaemon.AppModel.Internal; - -internal interface IAssemblyResolver -{ - public Assembly GetResolvedAssembly(); -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs index 838df9a57..42ffec497 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs @@ -1,4 +1,4 @@ -using NetDaemon.AppModel.Internal.Resolver; +using NetDaemon.AppModel.Internal.AppFactory; namespace NetDaemon.AppModel.Internal; @@ -8,7 +8,7 @@ internal sealed class ApplicationContext private readonly IServiceScope? _serviceScope; private bool _isDisposed; - public ApplicationContext(IServiceProvider serviceProvider, IAppInstance instance) + public ApplicationContext(IServiceProvider serviceProvider, IAppFactory appFactory) { // Create a new ServiceScope for all objects we create for this app // this makes sure they will all be disposed along with the app @@ -21,7 +21,7 @@ public ApplicationContext(IServiceProvider serviceProvider, IAppInstance instanc if (appScope != null) appScope.ApplicationContext = this; // Now create the actual app from the new scope - Instance = instance.Create(scopedProvider); + Instance = appFactory.Create(scopedProvider); } public object Instance { get; } diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs deleted file mode 100644 index da3c81103..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstance.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; - -namespace NetDaemon.AppModel.Internal.Resolver; - -internal class DynamicAppInstance : IAppInstance -{ - private readonly Type _type; - - public DynamicAppInstance(Type type) - { - _type = type; - - Id = AppInstanceHelper.GetAppId(type); - HasFocus = AppInstanceHelper.GetAppFocus(type); - } - - public string Id { get; } - - public bool HasFocus { get; } - - public object Create(IServiceProvider scopedServiceProvider) - { - return ActivatorUtilities.CreateInstance(scopedServiceProvider, _type); - } - - -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs deleted file mode 100644 index 3fc7a0313..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/DynamicAppInstanceResolver.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace NetDaemon.AppModel.Internal.Resolver; - -internal class DynamicAppInstanceResolver : IAppInstanceResolver -{ - private readonly IEnumerable _resolvers; - - public DynamicAppInstanceResolver(IEnumerable resolvers) - { - _resolvers = resolvers; - } - - public IReadOnlyCollection GetApps() - { - return _resolvers - .SelectMany(resolver => resolver.GetTypes()) - .Select(type => new DynamicAppInstance(type)) - .ToList(); - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs deleted file mode 100644 index f2427e67e..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstance.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace NetDaemon.AppModel.Internal.Resolver; - -internal class FactoryAppInstance : IAppInstance where TAppType : class -{ - private readonly Func _factoryFunc; - - public FactoryAppInstance(Func factoryFunc) - { - _factoryFunc = factoryFunc; - - Id = AppInstanceHelper.GetAppId(typeof(TAppType)); - HasFocus = AppInstanceHelper.GetAppFocus(typeof(TAppType)); - } - - public object Create(IServiceProvider scopedServiceProvider) - { - return _factoryFunc.Invoke(scopedServiceProvider); - } - - public string Id { get; } - - public bool HasFocus { get; } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs deleted file mode 100644 index f1c41116f..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/FactoryAppInstanceResolver.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace NetDaemon.AppModel.Internal.Resolver; - -internal class FactoryAppInstanceResolver : IAppInstanceResolver where TAppType : class -{ - private readonly Func _factoryFunc; - - public FactoryAppInstanceResolver(Func factoryFunc) - { - _factoryFunc = factoryFunc; - } - - public IReadOnlyCollection GetApps() - { - return new[] { new FactoryAppInstance(_factoryFunc) }; - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs deleted file mode 100644 index f690aec57..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstance.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace NetDaemon.AppModel.Internal.Resolver; - -internal interface IAppInstance -{ - string Id { get; } - - bool HasFocus { get; } - - object Create(IServiceProvider scopedServiceProvider); -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs deleted file mode 100644 index e4cba6f52..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/Instance/IAppInstanceResolver.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NetDaemon.AppModel.Internal.Resolver; - -internal interface IAppInstanceResolver -{ - IReadOnlyCollection GetApps(); -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs deleted file mode 100644 index ce5ab760c..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/AppTypeResolver.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Reflection; - -namespace NetDaemon.AppModel.Internal.TypeResolver; - -internal class AppTypeResolver : IAppTypeResolver -{ - private readonly IEnumerable _assemblyResolvers; - - public AppTypeResolver(IEnumerable assemblyResolvers) - { - _assemblyResolvers = assemblyResolvers; - } - - public IReadOnlyCollection GetTypes() - { - return _assemblyResolvers - .Select(resolver => resolver.GetResolvedAssembly()) - .SelectMany(assembly => assembly.GetTypes()) - .Where(IsNetDaemonAppType) - .ToList(); - } - - private static bool IsNetDaemonAppType(Type type) - { - if (!type.IsClass || !type.IsGenericType || !type.IsAbstract) - { - return false; - } - - return type.GetCustomAttribute() is not null; - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/IAppTypeResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/IAppTypeResolver.cs deleted file mode 100644 index ec6600a1e..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/IAppTypeResolver.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace NetDaemon.AppModel.Common.TypeResolver; - -/// -/// Implementers of this interface returns all types from -/// any source like the current assembly or a dynamically compiled assembly -/// -internal interface IAppTypeResolver -{ - /// - /// Returns all types - /// - IReadOnlyCollection GetTypes(); -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs b/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs deleted file mode 100644 index e623679a7..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/TypeResolver/SingleAppTypeResolver.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace NetDaemon.AppModel.Internal.TypeResolver; - -internal class SingleAppTypeResolver : IAppTypeResolver -{ - private readonly Type _appType; - - public SingleAppTypeResolver(Type appType) - { - _appType = appType; - } - - public IReadOnlyCollection GetTypes() - { - return new[] { _appType }; - } -} \ No newline at end of file From a0884618a4fc1dae2952d9f455f74caf4ea9c136 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 14:23:29 +0100 Subject: [PATCH 07/11] refactor: rename namespaces to be plural --- .../Common/Extensions/ServiceCollectionExtension.cs | 6 +++--- .../DynamicallyCompiledAppAssemblyProvider.cs | 2 +- .../IAppAssemblyProvider.cs | 2 +- .../SingleAppAssemblyProvider.cs | 2 +- .../{AppFactory => AppFactories}/AppFactoryHelper.cs | 2 +- .../{AppFactory => AppFactories}/FuncAppFactory.cs | 2 +- .../Internal/{AppFactory => AppFactories}/IAppFactory.cs | 2 +- .../{AppFactory => AppFactories}/TypeAppFactory.cs | 2 +- .../Internal/AppFactoryProvider/IAppFactoryProvider.cs | 8 -------- .../AssemblyAppFactoryProvider.cs | 6 +++--- .../Internal/AppFactoryProviders/IAppFactoryProvider.cs | 8 ++++++++ .../SingleAppFactoryProvider.cs | 4 ++-- .../NetDaemon.AppModel/Internal/AppModelContext.cs | 4 ++-- src/AppModel/NetDaemon.AppModel/Internal/Application.cs | 2 +- .../Internal/Context/ApplicationContext.cs | 2 +- 15 files changed, 27 insertions(+), 27 deletions(-) rename src/AppModel/NetDaemon.AppModel/Internal/{AppAssemblyProvider => AppAssemblyProviders}/DynamicallyCompiledAppAssemblyProvider.cs (94%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppAssemblyProvider => AppAssemblyProviders}/IAppAssemblyProvider.cs (64%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppAssemblyProvider => AppAssemblyProviders}/SingleAppAssemblyProvider.cs (83%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactory => AppFactories}/AppFactoryHelper.cs (91%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactory => AppFactories}/FuncAppFactory.cs (93%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactory => AppFactories}/IAppFactory.cs (70%) rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactory => AppFactories}/TypeAppFactory.cs (89%) delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactoryProvider => AppFactoryProviders}/AssemblyAppFactoryProvider.cs (84%) create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/IAppFactoryProvider.cs rename src/AppModel/NetDaemon.AppModel/Internal/{AppFactoryProvider => AppFactoryProviders}/SingleAppFactoryProvider.cs (74%) diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index c77ea3354..4ce6c6502 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -1,7 +1,7 @@ using System.Reflection; -using NetDaemon.AppModel.Internal.AppAssemblyProvider; -using NetDaemon.AppModel.Internal.AppFactory; -using NetDaemon.AppModel.Internal.AppFactoryProvider; +using NetDaemon.AppModel.Internal.AppAssemblyProviders; +using NetDaemon.AppModel.Internal.AppFactories; +using NetDaemon.AppModel.Internal.AppFactoryProviders; using NetDaemon.AppModel.Internal.Compiler; using NetDaemon.AppModel.Internal.Config; diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/DynamicallyCompiledAppAssemblyProvider.cs similarity index 94% rename from src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/DynamicallyCompiledAppAssemblyProvider.cs index be39a3586..ab631abc6 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/DynamicallyCompiledAppAssemblyProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/DynamicallyCompiledAppAssemblyProvider.cs @@ -1,7 +1,7 @@ using System.Reflection; using NetDaemon.AppModel.Internal.Compiler; -namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; +namespace NetDaemon.AppModel.Internal.AppAssemblyProviders; internal class DynamicallyCompiledAppAssemblyProvider : IAppAssemblyProvider, IDisposable { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/IAppAssemblyProvider.cs similarity index 64% rename from src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/IAppAssemblyProvider.cs index 621b27b95..3b856b123 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/IAppAssemblyProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/IAppAssemblyProvider.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; +namespace NetDaemon.AppModel.Internal.AppAssemblyProviders; internal interface IAppAssemblyProvider { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs similarity index 83% rename from src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs index 04caa8e2c..388c33647 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProvider/SingleAppAssemblyProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace NetDaemon.AppModel.Internal.AppAssemblyProvider; +namespace NetDaemon.AppModel.Internal.AppAssemblyProviders; internal class SingleAppAssemblyProvider : IAppAssemblyProvider { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs similarity index 91% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs index f113d7e8d..47cf4f9d7 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/AppFactoryHelper.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace NetDaemon.AppModel.Internal.AppFactory; +namespace NetDaemon.AppModel.Internal.AppFactories; internal static class AppFactoryHelper { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs similarity index 93% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs index cfc6d6c17..572adb94a 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/FuncAppFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs @@ -1,4 +1,4 @@ -namespace NetDaemon.AppModel.Internal.AppFactory; +namespace NetDaemon.AppModel.Internal.AppFactories; internal class FuncAppFactory : FuncAppFactory where TAppType : class { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/IAppFactory.cs similarity index 70% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactories/IAppFactory.cs index 8b3bc71c4..f93173b9b 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/IAppFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/IAppFactory.cs @@ -1,4 +1,4 @@ -namespace NetDaemon.AppModel.Internal.AppFactory; +namespace NetDaemon.AppModel.Internal.AppFactories; internal interface IAppFactory { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs similarity index 89% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs index 0dab2910c..b6b3a02d3 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactory/TypeAppFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs @@ -1,4 +1,4 @@ -namespace NetDaemon.AppModel.Internal.AppFactory; +namespace NetDaemon.AppModel.Internal.AppFactories; internal class TypeAppFactory : FuncAppFactory { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs deleted file mode 100644 index cac39c46f..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/IAppFactoryProvider.cs +++ /dev/null @@ -1,8 +0,0 @@ -using NetDaemon.AppModel.Internal.AppFactory; - -namespace NetDaemon.AppModel.Internal.AppFactoryProvider; - -internal interface IAppFactoryProvider -{ - IReadOnlyCollection GetAppFactories(); -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs similarity index 84% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs index 788000075..0b072bd35 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/AssemblyAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs @@ -1,8 +1,8 @@ using System.Reflection; -using NetDaemon.AppModel.Internal.AppAssemblyProvider; -using NetDaemon.AppModel.Internal.AppFactory; +using NetDaemon.AppModel.Internal.AppAssemblyProviders; +using NetDaemon.AppModel.Internal.AppFactories; -namespace NetDaemon.AppModel.Internal.AppFactoryProvider; +namespace NetDaemon.AppModel.Internal.AppFactoryProviders; internal class AssemblyAppFactoryProvider : IAppFactoryProvider { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/IAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/IAppFactoryProvider.cs new file mode 100644 index 000000000..51ad6eea5 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/IAppFactoryProvider.cs @@ -0,0 +1,8 @@ +using NetDaemon.AppModel.Internal.AppFactories; + +namespace NetDaemon.AppModel.Internal.AppFactoryProviders; + +internal interface IAppFactoryProvider +{ + IReadOnlyCollection GetAppFactories(); +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs similarity index 74% rename from src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs index e2b250e0f..52d186aa4 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProvider/SingleAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs @@ -1,6 +1,6 @@ -using NetDaemon.AppModel.Internal.AppFactory; +using NetDaemon.AppModel.Internal.AppFactories; -namespace NetDaemon.AppModel.Internal.AppFactoryProvider; +namespace NetDaemon.AppModel.Internal.AppFactoryProviders; internal class SingleAppFactoryProvider : IAppFactoryProvider { diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index 3b3c6999a..cd2b138ae 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -1,5 +1,5 @@ -using NetDaemon.AppModel.Internal.AppFactoryProvider; -using IAppFactory = NetDaemon.AppModel.Internal.AppFactory.IAppFactory; +using NetDaemon.AppModel.Internal.AppFactoryProviders; +using IAppFactory = NetDaemon.AppModel.Internal.AppFactories.IAppFactory; namespace NetDaemon.AppModel.Internal; diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs index e35b54e06..aabf6b7ae 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Application.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Application.cs @@ -1,4 +1,4 @@ -using NetDaemon.AppModel.Internal.AppFactory; +using NetDaemon.AppModel.Internal.AppFactories; namespace NetDaemon.AppModel.Internal; diff --git a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs index 42ffec497..c910f7413 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/Context/ApplicationContext.cs @@ -1,4 +1,4 @@ -using NetDaemon.AppModel.Internal.AppFactory; +using NetDaemon.AppModel.Internal.AppFactories; namespace NetDaemon.AppModel.Internal; From 88afb4400c75ed40dfc92d62cef88563b360430f Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 14:24:13 +0100 Subject: [PATCH 08/11] refactor: rename single app assembly provider --- .../Common/Extensions/ServiceCollectionExtension.cs | 2 +- .../{SingleAppAssemblyProvider.cs => AppAssemblyProvider.cs} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/{SingleAppAssemblyProvider.cs => AppAssemblyProvider.cs} (67%) diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index 4ce6c6502..d0f86abf9 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -43,7 +43,7 @@ public static IServiceCollection AddAppsFromAssembly(this IServiceCollection ser return services .AddAppModelIfNotExist() .AddAppTypeResolverIfNotExist() - .AddSingleton(new SingleAppAssemblyProvider(assembly)); + .AddSingleton(new AppAssemblyProvider(assembly)); } /// diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/AppAssemblyProvider.cs similarity index 67% rename from src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs rename to src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/AppAssemblyProvider.cs index 388c33647..18202647d 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/SingleAppAssemblyProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppAssemblyProviders/AppAssemblyProvider.cs @@ -2,11 +2,11 @@ namespace NetDaemon.AppModel.Internal.AppAssemblyProviders; -internal class SingleAppAssemblyProvider : IAppAssemblyProvider +internal class AppAssemblyProvider : IAppAssemblyProvider { private readonly Assembly _assembly; - public SingleAppAssemblyProvider(Assembly assembly) + public AppAssemblyProvider(Assembly assembly) { _assembly = assembly; } From 8ab12844a4f6717f3a20a1c2c11dc80c7a4a36bb Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 15:09:15 +0100 Subject: [PATCH 09/11] refactor: use factory methods instead of inheritance --- .../Extensions/ServiceCollectionExtension.cs | 2 +- .../Internal/AppFactories/AppFactoryHelper.cs | 24 ------- .../Internal/AppFactories/FuncAppFactory.cs | 64 +++++++++++++------ .../Internal/AppFactories/TypeAppFactory.cs | 18 ------ .../AssemblyAppFactoryProvider.cs | 2 +- .../SingleAppFactoryProvider.cs | 2 +- 6 files changed, 49 insertions(+), 63 deletions(-) delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs delete mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index d0f86abf9..9ecc85d81 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -30,7 +30,7 @@ public static IServiceCollection AddApp( { return services .AddAppModelIfNotExist() - .AddSingleton(new FuncAppFactory(factoryFunc, id, focus)); + .AddSingleton(FuncAppFactory.Create(factoryFunc, id, focus)); } /// diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs deleted file mode 100644 index 47cf4f9d7..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/AppFactoryHelper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Reflection; - -namespace NetDaemon.AppModel.Internal.AppFactories; - -internal static class AppFactoryHelper -{ - public static string GetAppId(Type type) - { - var attribute = type.GetCustomAttribute(); - var id = attribute?.Id ?? type.FullName; - - if (string.IsNullOrEmpty(id)) - { - throw new InvalidOperationException($"Could not get app id from {type}"); - } - - return id; - } - - public static bool GetAppFocus(Type type) - { - return type.GetCustomAttribute() is not null; - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs index 572adb94a..87e26262f 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs @@ -1,32 +1,60 @@ -namespace NetDaemon.AppModel.Internal.AppFactories; +using System.Reflection; -internal class FuncAppFactory : FuncAppFactory where TAppType : class -{ - public FuncAppFactory(Func factoryFunc, string? id = default, bool? focus = default) : - base( - factoryFunc, - id ?? AppFactoryHelper.GetAppId(typeof(TAppType)), - focus ?? AppFactoryHelper.GetAppFocus(typeof(TAppType)) - ) - { - } -} +namespace NetDaemon.AppModel.Internal.AppFactories; internal class FuncAppFactory : IAppFactory { - private readonly Func _factoryFunc; + private readonly Func _func; - public FuncAppFactory(Func factoryFunc, string id, bool focus) + private FuncAppFactory(Func func, Type type, string? id, bool? focus) { - _factoryFunc = factoryFunc; + _func = func; - Id = id; - HasFocus = focus; + Id = id ?? GetAppId(type); + HasFocus = focus ?? GetAppFocus(type); } + + private static string GetAppId(Type type) + { + var attribute = type.GetCustomAttribute(); + var id = attribute?.Id ?? type.FullName; + + if (string.IsNullOrEmpty(id)) + { + throw new InvalidOperationException($"Could not get app id from {type}"); + } - public object Create(IServiceProvider provider) => _factoryFunc.Invoke(provider); + return id; + } + + private static bool GetAppFocus(Type type) + { + return type.GetCustomAttribute() is not null; + } + + public object Create(IServiceProvider provider) + { + return _func.Invoke(provider); + } public string Id { get; } public bool HasFocus { get; } + + public static FuncAppFactory Create(Func func, + string? id = default, bool? focus = default) where TAppType : class + { + return new FuncAppFactory(func, typeof(TAppType), id, focus); + } + + public static FuncAppFactory Create(Type type, + string? id = default, bool? focus = default) + { + return new FuncAppFactory(CreateFactoryFunc(type), type, id, focus); + } + + private static Func CreateFactoryFunc(Type type) + { + return provider => ActivatorUtilities.CreateInstance(provider, type); + } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs deleted file mode 100644 index b6b3a02d3..000000000 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/TypeAppFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace NetDaemon.AppModel.Internal.AppFactories; - -internal class TypeAppFactory : FuncAppFactory -{ - public TypeAppFactory(Type type, string? id = default, bool? focus = default) : - base( - CreateFactoryFunc(type), - id ?? AppFactoryHelper.GetAppId(type), - focus ?? AppFactoryHelper.GetAppFocus(type) - ) - { - } - - private static Func CreateFactoryFunc(Type type) - { - return provider => ActivatorUtilities.CreateInstance(provider, type); - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs index 0b072bd35..0d62224d6 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs @@ -19,7 +19,7 @@ public IReadOnlyCollection GetAppFactories() .Select(resolver => resolver.GetAppAssembly()) .SelectMany(assembly => assembly.GetTypes()) .Where(IsNetDaemonAppType) - .Select(type => new TypeAppFactory(type)) + .Select(type => FuncAppFactory.Create(type)) .ToList(); } diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs index 52d186aa4..5b9475363 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs @@ -13,6 +13,6 @@ public SingleAppFactoryProvider(Type appType) public IReadOnlyCollection GetAppFactories() { - return new[] { new TypeAppFactory(_appType) }; + return new[] { FuncAppFactory.Create(_appType) }; } } \ No newline at end of file From 99f0ff662f178e852b7d3e4ba5d4bcc44a0f75d6 Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 17:21:15 +0100 Subject: [PATCH 10/11] fix: update tests --- .../CombinedAppAssemblyProviderTests.cs | 41 +++ .../DynamicAppAssemblyProviderTests.cs | 38 +++ .../LocalAppAssemblyProviderTests.cs | 37 +++ .../AppFactories/DynamicAppFactoryTests.cs | 34 +++ .../AppFactories/LocalAppFactoryTests.cs | 131 ++++++++++ .../CombinedAppFactoryProviderTests.cs | 37 +++ .../DynamicAppFactoryProviderTests.cs | 47 ++++ .../LocalAppFactoryProviderTests.cs | 112 ++++++++ .../NetDaemon.AppModel.Tests/AppModelTests.cs | 6 +- .../AssemblyResolver/AssemblyResolverTests.cs | 129 --------- .../Context/ApplicationScopeTests.cs | 4 +- .../Fixtures/Local/LocalApp.cs | 2 + .../Fixtures/Local/LocalAppWithId.cs | 3 +- .../Helpers/FakeOptionsExtensions.cs | 15 ++ .../TypeResolver/TypeResolverTests.cs | 244 ------------------ .../Extensions/ServiceCollectionExtension.cs | 12 +- .../Internal/AppFactories/FuncAppFactory.cs | 2 +- .../AssemblyAppFactoryProvider.cs | 2 +- .../FuncAppFactoryProvider.cs | 11 + .../SingleAppFactoryProvider.cs | 23 +- 20 files changed, 538 insertions(+), 392 deletions(-) create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/CombinedAppAssemblyProviderTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/DynamicAppAssemblyProviderTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/LocalAppAssemblyProviderTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppFactories/DynamicAppFactoryTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/CombinedAppFactoryProviderTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/DynamicAppFactoryProviderTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs delete mode 100644 src/AppModel/NetDaemon.AppModel.Tests/AssemblyResolver/AssemblyResolverTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel.Tests/Helpers/FakeOptionsExtensions.cs delete mode 100644 src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs create mode 100644 src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/FuncAppFactoryProvider.cs diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/CombinedAppAssemblyProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/CombinedAppAssemblyProviderTests.cs new file mode 100644 index 000000000..e9a1f62eb --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/CombinedAppAssemblyProviderTests.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using LocalApps; +using NetDaemon.AppModel.Internal.AppAssemblyProviders; +using NetDaemon.AppModel.Tests.Helpers; + +namespace NetDaemon.AppModel.Tests.AppAssemblyProviders; + +public class CombinedAppAssemblyProviderTests +{ + [Fact] + public void TestCombinedAssembliesAreProvided() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalApp).Assembly); + + // ACT + var assemblyProviders = serviceProvider.GetRequiredService>(); + var assemblies = assemblyProviders.Select(provider => provider.GetAppAssembly()).ToList(); + + // ASSERT + assemblies.Should().Contain(assembly => CheckAssemblyHasType(assembly, MyAppLocalApp.Id)); + assemblies.Should().Contain(assembly => CheckAssemblyHasType(assembly, "Apps.MyApp")); + } + + private static bool CheckAssemblyHasType(Assembly assembly, string name) + { + var type = assembly.GetType(name); + return type?.FullName == name; + } + + private static IServiceProvider CreateServiceProvider(Assembly assembly) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddFakeOptions("Dynamic"); + serviceCollection.AddAppsFromAssembly(assembly); + serviceCollection.AddAppsFromSource(); + + return serviceCollection.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/DynamicAppAssemblyProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/DynamicAppAssemblyProviderTests.cs new file mode 100644 index 000000000..38349bcd7 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/DynamicAppAssemblyProviderTests.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using NetDaemon.AppModel.Internal.AppAssemblyProviders; +using NetDaemon.AppModel.Tests.Helpers; + +namespace NetDaemon.AppModel.Tests.AppAssemblyProviders; + +public class DynamicAppAssemblyProviderTests +{ + [Fact] + public void TestDynamicallyCompiledAssembliesAreProvided() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var assemblyProviders = serviceProvider.GetRequiredService>(); + var assemblies = assemblyProviders.Select(provider => provider.GetAppAssembly()).ToList(); + + // ASSERT + assemblies.Should().Contain(assembly => CheckAssemblyHasType(assembly, "Apps.MyApp")); + } + + private static bool CheckAssemblyHasType(Assembly assembly, string name) + { + var type = assembly.GetType(name); + return type?.FullName == name; + } + + private static IServiceProvider CreateServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddFakeOptions("Dynamic"); + serviceCollection.AddAppsFromSource(); + + return serviceCollection.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/LocalAppAssemblyProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/LocalAppAssemblyProviderTests.cs new file mode 100644 index 000000000..96a4785eb --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppAssemblyProviders/LocalAppAssemblyProviderTests.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using LocalApps; +using NetDaemon.AppModel.Internal.AppAssemblyProviders; + +namespace NetDaemon.AppModel.Tests.AppAssemblyProviders; + +public class LocalAppAssemblyProviderTests +{ + [Fact] + public void TestLocalAppAssembliesAreProvided() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalApp).Assembly); + + // ACT + var assemblyProviders = serviceProvider.GetRequiredService>(); + var assemblies = assemblyProviders.Select(provider => provider.GetAppAssembly()).ToList(); + + // ASSERT + assemblies.Should().Contain(assembly => CheckAssemblyHasType(assembly, MyAppLocalApp.Id)); + } + + private static bool CheckAssemblyHasType(Assembly assembly, string name) + { + var type = assembly.GetType(name); + return type?.FullName == name; + } + + private static IServiceProvider CreateServiceProvider(Assembly assembly) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddAppsFromAssembly(assembly); + + return serviceCollection.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/DynamicAppFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/DynamicAppFactoryTests.cs new file mode 100644 index 000000000..7651ed1d1 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/DynamicAppFactoryTests.cs @@ -0,0 +1,34 @@ +using NetDaemon.AppModel.Internal.AppFactoryProviders; +using NetDaemon.AppModel.Tests.Helpers; + +namespace NetDaemon.AppModel.Tests.AppFactories; + +public class DynamicAppFactoryTests +{ + [Fact] + public void TestDynamicAppFactoryCreatesApp() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == "Apps.InjectedApp"); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.GetType().FullName.Should().BeEquivalentTo("Apps.InjectedApp"); + } + + private static IServiceProvider CreateServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddFakeOptions("DynamicWithServiceCollection"); + serviceCollection.AddAppsFromSource(); + + return serviceCollection.BuildServiceProvider(); + } +} diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs new file mode 100644 index 000000000..8b60e60e9 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs @@ -0,0 +1,131 @@ +using System.Reflection; +using LocalApps; +using NetDaemon.AppModel.Internal.AppFactoryProviders; + +namespace NetDaemon.AppModel.Tests.AppFactories; + +public class LocalAppFactoryTests +{ + [Fact] + public void TestLocalAppFactoryCreatesApp() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalAppWithId).Assembly); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == MyAppLocalAppWithId.Id); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + [Fact] + public void TestCustomAppFactoryCreatesAppWithoutId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalApp(Mock.Of>())); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == MyAppLocalApp.Id); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + [Fact] + public void TestCustomAppFactoryCreatesAppWithId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId()); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == MyAppLocalAppWithId.Id); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + [Fact] + public void TestCustomAppFactoryCreatesWithCustomId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId"); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == "CustomId"); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + [Fact] + public void TestCustomAppFactoryCreatesWithCustomFocusTrue() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId", true); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == "CustomId" && factory.HasFocus); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + [Fact] + public void TestCustomAppFactoryCreatesWithCustomFocusFalse() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId", false); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + var appFactory = appFactories.Single(factory => factory.Id == "CustomId" && factory.HasFocus == false); + var appInstance = appFactory.Create(serviceProvider); + + // ASSERT + appInstance.Should().NotBeNull(); + appInstance.Should().BeOfType(); + } + + private static IServiceProvider CreateServiceProvider(Assembly assembly) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddAppsFromAssembly(assembly); + + return serviceCollection.BuildServiceProvider(); + } + + private static IServiceProvider CreateServiceProvider( + Func func, + string? id = default, + bool? focus = default) where TAppType : class + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddApp(func, id, focus); + + return serviceCollection.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/CombinedAppFactoryProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/CombinedAppFactoryProviderTests.cs new file mode 100644 index 000000000..e1b03c7d6 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/CombinedAppFactoryProviderTests.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using LocalApps; +using NetDaemon.AppModel.Internal.AppFactoryProviders; +using NetDaemon.AppModel.Tests.Helpers; + +namespace NetDaemon.AppModel.Tests.AppFactoryProviders; + +public class CombinedAppFactoryProviderTests +{ + [Fact] + public void TestCombinedAssemblyAppFactoriesAreProvided() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalAppWithId).Assembly); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id); + appFactories.Should().Contain(factory => factory.Id == MyAppLocalAppWithId.Id); + appFactories.Should().Contain(factory => factory.Id == "Apps.MyApp"); + } + + private static IServiceProvider CreateServiceProvider(Assembly assembly) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddFakeOptions("Dynamic"); + serviceCollection.AddAppFromType(); + serviceCollection.AddAppsFromAssembly(assembly); + serviceCollection.AddAppsFromSource(); + + return serviceCollection.BuildServiceProvider(); + } +} diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/DynamicAppFactoryProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/DynamicAppFactoryProviderTests.cs new file mode 100644 index 000000000..21d3a3829 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/DynamicAppFactoryProviderTests.cs @@ -0,0 +1,47 @@ +using NetDaemon.AppModel.Internal.AppFactoryProviders; +using NetDaemon.AppModel.Tests.Helpers; + +namespace NetDaemon.AppModel.Tests.AppFactoryProviders; + +public class DynamicAppFactoryProviderTests +{ + [Fact] + public void TestDynamicAssemblyAppFactoriesAreProvidedWithoutFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == "Apps.NonFocusApp" && + factory.HasFocus == false); + } + + [Fact] + public void TestDynamicAssemblyAppFactoriesAreProvidedWithFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == "Apps.MyFocusApp" && + factory.HasFocus == true); + } + + private static IServiceProvider CreateServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddFakeOptions("DynamicWithFocus"); + serviceCollection.AddAppsFromSource(); + + return serviceCollection.BuildServiceProvider(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs new file mode 100644 index 000000000..4f09a6bfd --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs @@ -0,0 +1,112 @@ +using System.Reflection; +using LocalApps; +using NetDaemon.AppModel.Internal.AppFactoryProviders; + +namespace NetDaemon.AppModel.Tests.AppFactoryProviders; + +public class LocalAppFactoryProviderTests +{ + [Fact] + public void TestLocalAssemblyAppFactoriesAreProvidedWithFullNameId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalApp).Assembly); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id); + } + + [Fact] + public void TestLocalAssemblyAppFactoriesAreProvidedWithCustomId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalAppWithId).Assembly); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalAppWithId.Id); + } + + [Fact] + public void TestLocalAssemblyAppFactoriesAreProvidedWithoutFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(typeof(MyAppLocalApp).Assembly); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id && + factory.HasFocus == false); + } + + [Fact] + public void TestLocalSingleAppFactoriesAreProvidedWithFullNameId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id); + } + + [Fact] + public void TestLocalSingleAppFactoriesAreProvidedWithCustomId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalAppWithId.Id); + } + + [Fact] + public void TestLocalSingleAppFactoriesAreProvidedWithoutFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id && + factory.HasFocus == false); + } + + private static IServiceProvider CreateServiceProvider(Assembly assembly) + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddAppsFromAssembly(assembly); + + return serviceCollection.BuildServiceProvider(); + } + + private static IServiceProvider CreateServiceProvider() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddAppFromType(); + + return serviceCollection.BuildServiceProvider(); + } +} diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs index 229d8af77..cbfe88931 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NetDaemon.AppModel.Internal; -using NetDaemon.AppModel.Internal.Resolver; +using NetDaemon.AppModel.Internal.AppFactories; using NetDaemon.AppModel.Tests.Helpers; namespace NetDaemon.AppModel.Tests.Internal; @@ -141,10 +141,10 @@ public async Task TestSetStateToRunningShouldThrowException() // ARRANGE var provider = Mock.Of(); var logger = Mock.Of>(); - var instance = Mock.Of(); + var factory = Mock.Of(); // ACT - var app = new Application(provider, logger, instance); + var app = new Application(provider, logger, factory); // CHECK await Assert.ThrowsAsync(() => app.SetStateAsync(ApplicationState.Running)); diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AssemblyResolver/AssemblyResolverTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AssemblyResolver/AssemblyResolverTests.cs deleted file mode 100644 index 937151d75..000000000 --- a/src/AppModel/NetDaemon.AppModel.Tests/AssemblyResolver/AssemblyResolverTests.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Reflection; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; -using NetDaemon.AppModel.Internal; -using NetDaemon.AppModel.Internal.Compiler; -using NetDaemon.AppModel.Tests.Helpers; - -namespace NetDaemon.AppModel.Tests.Internal.TypeResolver; - -internal class ResolvedLocalApp -{ -} - -public class AssemblyResolverTests -{ - [Fact] - public void TestLocalAssemblyShouldBeResolved() - { - var serviceCollection = new ServiceCollection(); - - // get apps from test project - serviceCollection.AddAppsFromAssembly(Assembly.GetCallingAssembly()); - - serviceCollection.AddLogging(); - var provider = serviceCollection.BuildServiceProvider(); - - var assemblyResolvers = provider.GetService>() ?? - throw new NullReferenceException("Not expected null"); - assemblyResolvers.Should().HaveCount(1); - } - - [Fact] - public void TestDynamicallyCompiledAssemblyShouldBeResolved() - { - var syntaxTreeResolverMock = new Mock(); - // We setup the mock to return a pre-built syntax tree with a fake class - syntaxTreeResolverMock - .Setup( - n => n.GetSyntaxTrees() - ) - .Returns( - () => - { - var result = new List(); - var sourceText = SourceText.From( - @" - public class FakeClass - { - - } - " - , Encoding.UTF8); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: "fakepath.cs") - ?? throw new NullReferenceException("unexpected null reference"); - - result.Add( - syntaxTree - ); - return result; - } - ); - - var serviceCollection = new ServiceCollection(); - - serviceCollection - .AddSingleton(_ => syntaxTreeResolverMock.Object); - serviceCollection.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, - Path.Combine(AppContext.BaseDirectory, "Fixtures/Dynamic")))); - serviceCollection.AddLogging(); - serviceCollection.AddAppsFromSource(); - var provider = serviceCollection.BuildServiceProvider(); - - var assemblyResolvers = provider.GetService>() ?? - throw new NullReferenceException("Not expected null"); - assemblyResolvers.Should().HaveCount(1); - } - - [Fact] - public void TestBothLocalAndDynamicallyResolvedAssembliesShouldBeResolved() - { - var syntaxTreeResolverMock = new Mock(); - // We setup the mock to return a prebuilt syntax tree with a fake class - syntaxTreeResolverMock - .Setup( - n => n.GetSyntaxTrees() - ) - .Returns( - () => - { - var result = new List(); - var sourceText = SourceText.From( - @" - public class FakeClass - { - - } - " - , Encoding.UTF8); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: "fakepath.cs") - ?? throw new NullReferenceException("unexpected null reference"); - - result.Add( - syntaxTree - ); - return result; - } - ); - - var serviceCollection = new ServiceCollection(); - // get apps from test project - serviceCollection.AddLogging(); - serviceCollection.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, - Path.Combine(AppContext.BaseDirectory, "Fixtures/Dynamic")))); - serviceCollection.AddAppsFromAssembly(Assembly.GetCallingAssembly()); - serviceCollection - .AddSingleton(_ => syntaxTreeResolverMock.Object); - serviceCollection.AddAppsFromSource(); - - serviceCollection.AddLogging(); - var provider = serviceCollection.BuildServiceProvider(); - - var assemblyResolvers = provider.GetService>() ?? - throw new NullReferenceException("Not expected null"); - assemblyResolvers.Should().HaveCount(2); - } -} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs index a531f4f23..dd6017646 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Context/ApplicationScopeTests.cs @@ -1,5 +1,5 @@ using NetDaemon.AppModel.Internal; -using NetDaemon.AppModel.Internal.Resolver; +using NetDaemon.AppModel.Internal.AppFactories; namespace NetDaemon.AppModel.Tests.Context; @@ -24,7 +24,7 @@ public void TestInitializedScopeReturnsOk() { ApplicationContext = new ApplicationContext( new ServiceCollection().BuildServiceProvider(), - Mock.Of() + Mock.Of() ) }; var ctx = scope.ApplicationContext; diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalApp.cs b/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalApp.cs index b5a932cd1..d8a0e5900 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalApp.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalApp.cs @@ -10,6 +10,8 @@ public class LocalTestSettings [NetDaemonApp] public class MyAppLocalApp { + public static readonly string Id = typeof(MyAppLocalApp).FullName!; + public MyAppLocalApp(IAppConfig settings) { Settings = settings.Value; diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalAppWithId.cs b/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalAppWithId.cs index 3b0b91d47..b41c855d0 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalAppWithId.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/Fixtures/Local/LocalAppWithId.cs @@ -1,6 +1,7 @@ namespace LocalApps; -[NetDaemonApp(Id = "SomeId")] +[NetDaemonApp(Id = Id)] public class MyAppLocalAppWithId { + public const string Id = "SomeId"; } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/Helpers/FakeOptionsExtensions.cs b/src/AppModel/NetDaemon.AppModel.Tests/Helpers/FakeOptionsExtensions.cs new file mode 100644 index 000000000..728647cdd --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel.Tests/Helpers/FakeOptionsExtensions.cs @@ -0,0 +1,15 @@ +namespace NetDaemon.AppModel.Tests.Helpers; + +internal static class FakeOptionsExtensions +{ + public static IServiceCollection AddFakeOptions(this IServiceCollection services, string name) + { + return services.AddTransient(_ => CreateFakeOptions(name)); + } + + private static IOptions CreateFakeOptions(string name) + { + var path = Path.Combine(AppContext.BaseDirectory, Path.Combine(AppContext.BaseDirectory, $"Fixtures/{name}")); + return new FakeOptions(path); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs deleted file mode 100644 index 0683ef6cc..000000000 --- a/src/AppModel/NetDaemon.AppModel.Tests/TypeResolver/TypeResolverTests.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Reflection; -using LocalApps; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Text; -using NetDaemon.AppModel.Common.TypeResolver; -using NetDaemon.AppModel.Internal; -using NetDaemon.AppModel.Internal.Compiler; -using NetDaemon.AppModel.Tests.Helpers; - -namespace NetDaemon.AppModel.Tests.Internal.TypeResolver; - -internal class LocalApp -{ -} - -public class TypeResolverTests -{ - [Fact] - public void TestLocalTypeResolverHasType() - { - var serviceCollection = new ServiceCollection(); - - // get apps from test project - serviceCollection.AddAppsFromAssembly(Assembly.GetExecutingAssembly()); - serviceCollection.AddLogging(); - var provider = serviceCollection.BuildServiceProvider(); - - var typeResolver = provider.GetService() ?? - throw new NullReferenceException("Not expected null"); - - var t = typeResolver.GetTypes().Where(n => n.Name == "LocalApp").ToList(); - t.Should().HaveCount(1); - } - - [Fact] - public void TestDynamicCompiledTypeResolverHasType() - { - var syntaxTreeResolverMock = new Mock(); - // We setup the mock to return a prebuilt syntax tree with a fake class - syntaxTreeResolverMock - .Setup( - n => n.GetSyntaxTrees() - ) - .Returns( - () => - { - var result = new List(); - var sourceText = SourceText.From( - @" - public class FakeClass - { - - } - " - , Encoding.UTF8); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: "fakepath.cs") - ?? throw new NullReferenceException("unexpected null reference"); - - result.Add( - syntaxTree - ); - return result; - } - ); - - var serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(); - serviceCollection.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, - Path.Combine(AppContext.BaseDirectory, "Fixtures/Dynamic")))); - serviceCollection.AddSingleton(_ => syntaxTreeResolverMock.Object); - serviceCollection.AddAppModelIfNotExist(); - serviceCollection.AddAppTypeResolverIfNotExist(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => s.GetRequiredService()); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => - s.GetRequiredService()); - - - var provider = serviceCollection.BuildServiceProvider(); - - var typeResolver = provider.GetService() ?? - throw new NullReferenceException("Not expected null"); - - var t = typeResolver.GetTypes().Where(n => n.Name == "FakeClass").ToList(); - t.Should().HaveCount(1); - } - - [Fact] - public void TestDynamicCompiledTypeResolverUsedMultipleTimesHasType() - { - var syntaxTreeResolverMock = new Mock(); - // We setup the mock to return a prebuilt syntax tree with a fake class - syntaxTreeResolverMock - .Setup( - n => n.GetSyntaxTrees() - ) - .Returns( - () => - { - var result = new List(); - var sourceText = SourceText.From( - @" - public class FakeClass - { - - } - " - , Encoding.UTF8); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: "fakepath.cs") - ?? throw new NullReferenceException("unexpected null reference"); - - result.Add( - syntaxTree - ); - return result; - } - ); - - var serviceCollection = new ServiceCollection(); - - serviceCollection.AddLogging(); - serviceCollection.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, - Path.Combine(AppContext.BaseDirectory, "Fixtures/Dynamic")))); - serviceCollection.AddSingleton(_ => syntaxTreeResolverMock.Object); - serviceCollection.AddAppModelIfNotExist(); - serviceCollection.AddAppTypeResolverIfNotExist(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => s.GetRequiredService()); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => - s.GetRequiredService()); - - - var provider = serviceCollection.BuildServiceProvider(); - - var typeResolver = provider.GetService() ?? - throw new NullReferenceException("Not expected null"); - - var t = typeResolver.GetTypes().Where(n => n.Name == "FakeClass").ToList(); - t.Should().HaveCount(1); - t = typeResolver.GetTypes().Where(n => n.Name == "FakeClass").ToList(); - t.Should().HaveCount(1); - } - - [Fact] - public void AssemblyResolverShouldDisposeWithoutError() - { - var syntaxTreeResolverMock = new Mock(); - // We setup the mock to return a prebuilt syntax tree with a fake class - syntaxTreeResolverMock - .Setup( - n => n.GetSyntaxTrees() - ) - .Returns( - () => - { - var result = new List(); - var sourceText = SourceText.From( - @" - public class FakeClass - { - - } - " - , Encoding.UTF8); - var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, path: "fakepath.cs") - ?? throw new NullReferenceException("unexpected null reference"); - - result.Add( - syntaxTree - ); - return result; - } - ); - - var serviceCollection = new ServiceCollection(); - - serviceCollection.AddLogging(); - serviceCollection.AddTransient>( - _ => new FakeOptions(Path.Combine(AppContext.BaseDirectory, - Path.Combine(AppContext.BaseDirectory, "Fixtures/Dynamic")))); - serviceCollection.AddSingleton(_ => syntaxTreeResolverMock.Object); - serviceCollection.AddAppModelIfNotExist(); - serviceCollection.AddAppTypeResolverIfNotExist(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => s.GetRequiredService()); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(s => - s.GetRequiredService()); - - - var provider = serviceCollection.BuildServiceProvider(); - - var typeResolver = provider.GetService() ?? - throw new NullReferenceException("Not expected null"); - - var t = typeResolver.GetTypes().Where(n => n.Name == "FakeClass").ToList(); - t.Should().HaveCount(1); - - var assemblyResolver = provider.GetRequiredService(); - - // Assert Dispose does not throw - var exception = Record.Exception(() => assemblyResolver.Dispose()); - Assert.Null(exception); - } - - [Fact] - public void TestAddAppFromTypeShouldLoadSingleApp() - { - var serviceCollection = new ServiceCollection(); - - // get apps from test project - serviceCollection.AddAppFromType(typeof(MyAppLocalApp)); - - serviceCollection.AddLogging(); - var provider = serviceCollection.BuildServiceProvider(); - - var appResolvers = provider.GetRequiredService>() ?? - throw new NullReferenceException("Not expected null"); - appResolvers.Should().HaveCount(1); - appResolvers.First().GetTypes().Should().BeEquivalentTo(new[] {typeof(MyAppLocalApp)}); - } - - [Fact] - public void TestAddAppFromTypeGenericShouldLoadSingleApp() - { - var serviceCollection = new ServiceCollection(); - - // get apps from test project - serviceCollection.AddAppFromType(); - - serviceCollection.AddLogging(); - var provider = serviceCollection.BuildServiceProvider(); - - var appResolvers = provider.GetRequiredService>() ?? - throw new NullReferenceException("Not expected null"); - appResolvers.Should().HaveCount(1); - appResolvers.First().GetTypes().Should().BeEquivalentTo(new[] {typeof(MyAppLocalApp)}); - } -} diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index 9ecc85d81..0e6e0356f 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -30,7 +30,7 @@ public static IServiceCollection AddApp( { return services .AddAppModelIfNotExist() - .AddSingleton(FuncAppFactory.Create(factoryFunc, id, focus)); + .AddSingleton(SingleAppFactoryProvider.Create(factoryFunc, id, focus)); } /// @@ -42,7 +42,7 @@ public static IServiceCollection AddAppsFromAssembly(this IServiceCollection ser { return services .AddAppModelIfNotExist() - .AddAppTypeResolverIfNotExist() + .AddAppFactoryIfNotExists() .AddSingleton(new AppAssemblyProvider(assembly)); } @@ -67,7 +67,7 @@ public static IServiceCollection AddAppFromType(this IServiceCollection services { return services .AddAppModelIfNotExist() - .AddSingleton(new SingleAppFactoryProvider(type)); + .AddSingleton(SingleAppFactoryProvider.Create(type)); } /// @@ -79,7 +79,7 @@ public static IServiceCollection AddAppsFromSource(this IServiceCollection servi // We make sure we only add AppModel services once services .AddAppModelIfNotExist() - .AddAppTypeResolverIfNotExist() + .AddAppFactoryIfNotExists() .AddSingleton() .AddSingleton(s => s.GetRequiredService()) .AddSingleton() @@ -113,7 +113,7 @@ private static IServiceCollection RegisterDynamicFunctions(this IServiceCollecti return services; } - internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection services) + private static IServiceCollection AddAppModelIfNotExist(this IServiceCollection services) { // Check if we already registered if (services.Any(n => n.ImplementationType == typeof(AppModelImpl))) @@ -130,7 +130,7 @@ internal static IServiceCollection AddAppModelIfNotExist(this IServiceCollection return services; } - internal static IServiceCollection AddAppTypeResolverIfNotExist(this IServiceCollection services) + private static IServiceCollection AddAppFactoryIfNotExists(this IServiceCollection services) { if (services.Any(descriptor => descriptor.ImplementationType == typeof(AssemblyAppFactoryProvider))) return services; diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs index 87e26262f..a19f7b604 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactories/FuncAppFactory.cs @@ -13,7 +13,7 @@ private FuncAppFactory(Func func, Type type, string? i Id = id ?? GetAppId(type); HasFocus = focus ?? GetAppFocus(type); } - + private static string GetAppId(Type type) { var attribute = type.GetCustomAttribute(); diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs index 0d62224d6..f3d962ebb 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/AssemblyAppFactoryProvider.cs @@ -25,7 +25,7 @@ public IReadOnlyCollection GetAppFactories() private static bool IsNetDaemonAppType(Type type) { - if (!type.IsClass || !type.IsGenericType || !type.IsAbstract) + if (!type.IsClass || type.IsGenericType || type.IsAbstract) { return false; } diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/FuncAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/FuncAppFactoryProvider.cs new file mode 100644 index 000000000..d6883c46b --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/FuncAppFactoryProvider.cs @@ -0,0 +1,11 @@ +using NetDaemon.AppModel.Internal.AppFactories; + +namespace NetDaemon.AppModel.Internal.AppFactoryProviders; + +internal class FuncAppFactoryProvider : IAppFactoryProvider +{ + public IReadOnlyCollection GetAppFactories() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs index 5b9475363..79d6a6a4f 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppFactoryProviders/SingleAppFactoryProvider.cs @@ -2,17 +2,30 @@ namespace NetDaemon.AppModel.Internal.AppFactoryProviders; -internal class SingleAppFactoryProvider : IAppFactoryProvider +internal sealed class SingleAppFactoryProvider : IAppFactoryProvider { - private readonly Type _appType; + private readonly IAppFactory _factory; - public SingleAppFactoryProvider(Type appType) + private SingleAppFactoryProvider(IAppFactory factory) { - _appType = appType; + _factory = factory; } public IReadOnlyCollection GetAppFactories() { - return new[] { FuncAppFactory.Create(_appType) }; + return new[] { _factory }; + } + + public static IAppFactoryProvider Create(Func func, + string? id = default, bool? focus = default) where TAppType : class + { + var factory = FuncAppFactory.Create(func, id, focus); + return new SingleAppFactoryProvider(factory); + } + + public static IAppFactoryProvider Create(Type type, string? id = default, bool? focus = default) + { + var factory = FuncAppFactory.Create(type, id, focus); + return new SingleAppFactoryProvider(factory); } } \ No newline at end of file From 78bf79e66d4f69113bf50b46d9512f400e8f035c Mon Sep 17 00:00:00 2001 From: BeeHiveJava Date: Sun, 30 Jan 2022 17:52:19 +0100 Subject: [PATCH 11/11] feat: move some tests around --- .../AppFactories/LocalAppFactoryTests.cs | 70 +--------------- .../LocalAppFactoryProviderTests.cs | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 69 deletions(-) diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs index 8b60e60e9..d9a066206 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactories/LocalAppFactoryTests.cs @@ -24,7 +24,7 @@ public void TestLocalAppFactoryCreatesApp() } [Fact] - public void TestCustomAppFactoryCreatesAppWithoutId() + public void TestCustomAppFactoryCreatesApp() { // ARRANGE var serviceProvider = CreateServiceProvider(_ => new MyAppLocalApp(Mock.Of>())); @@ -40,74 +40,6 @@ public void TestCustomAppFactoryCreatesAppWithoutId() appInstance.Should().BeOfType(); } - [Fact] - public void TestCustomAppFactoryCreatesAppWithId() - { - // ARRANGE - var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId()); - - // ACT - var appFactoryProviders = serviceProvider.GetRequiredService>(); - var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); - var appFactory = appFactories.Single(factory => factory.Id == MyAppLocalAppWithId.Id); - var appInstance = appFactory.Create(serviceProvider); - - // ASSERT - appInstance.Should().NotBeNull(); - appInstance.Should().BeOfType(); - } - - [Fact] - public void TestCustomAppFactoryCreatesWithCustomId() - { - // ARRANGE - var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId"); - - // ACT - var appFactoryProviders = serviceProvider.GetRequiredService>(); - var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); - var appFactory = appFactories.Single(factory => factory.Id == "CustomId"); - var appInstance = appFactory.Create(serviceProvider); - - // ASSERT - appInstance.Should().NotBeNull(); - appInstance.Should().BeOfType(); - } - - [Fact] - public void TestCustomAppFactoryCreatesWithCustomFocusTrue() - { - // ARRANGE - var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId", true); - - // ACT - var appFactoryProviders = serviceProvider.GetRequiredService>(); - var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); - var appFactory = appFactories.Single(factory => factory.Id == "CustomId" && factory.HasFocus); - var appInstance = appFactory.Create(serviceProvider); - - // ASSERT - appInstance.Should().NotBeNull(); - appInstance.Should().BeOfType(); - } - - [Fact] - public void TestCustomAppFactoryCreatesWithCustomFocusFalse() - { - // ARRANGE - var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId", false); - - // ACT - var appFactoryProviders = serviceProvider.GetRequiredService>(); - var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); - var appFactory = appFactories.Single(factory => factory.Id == "CustomId" && factory.HasFocus == false); - var appInstance = appFactory.Create(serviceProvider); - - // ASSERT - appInstance.Should().NotBeNull(); - appInstance.Should().BeOfType(); - } - private static IServiceProvider CreateServiceProvider(Assembly assembly) { var serviceCollection = new ServiceCollection(); diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs index 4f09a6bfd..884da711c 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppFactoryProviders/LocalAppFactoryProviderTests.cs @@ -92,6 +92,78 @@ public void TestLocalSingleAppFactoriesAreProvidedWithoutFocus() factory.HasFocus == false); } + [Fact] + public void TestLocalCustomAppFactoriesAreProvidedWithFullNameId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalApp(Mock.Of>())); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id); + } + + [Fact] + public void TestLocalCustomAppFactoriesAreProvidedWithCustomId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId()); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalAppWithId.Id); + } + + [Fact] + public void TestLocalCustomAppFactoriesAreProvidedWithCustomProvidedId() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalAppWithId(), "CustomId"); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == "CustomId"); + } + + [Fact] + public void TestLocalCustomAppFactoriesAreProvidedWithoutFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalApp(Mock.Of>())); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id && + factory.HasFocus == false); + } + + [Fact] + public void TestLocalCustomAppFactoriesAreProvidedWithFocus() + { + // ARRANGE + var serviceProvider = CreateServiceProvider(_ => new MyAppLocalApp(Mock.Of>()), focus: true); + + // ACT + var appFactoryProviders = serviceProvider.GetRequiredService>(); + var appFactories = appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); + + // ASSERT + appFactories.Should().Contain(factory => factory.Id == MyAppLocalApp.Id && + factory.HasFocus == true); + } + private static IServiceProvider CreateServiceProvider(Assembly assembly) { var serviceCollection = new ServiceCollection(); @@ -109,4 +181,16 @@ private static IServiceProvider CreateServiceProvider() return serviceCollection.BuildServiceProvider(); } + + private static IServiceProvider CreateServiceProvider( + Func func, + string? id = default, + bool? focus = default) where TAppType : class + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(); + serviceCollection.AddApp(func, id, focus); + + return serviceCollection.BuildServiceProvider(); + } }