diff --git a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs index cbfe88931..ac2724139 100644 --- a/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs +++ b/src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs @@ -255,6 +255,7 @@ public async Task TestFocusShouldAlwaysLoadAppIfIndependentOfStateManager(Applic fakeAppStateManager.Setup(n => n.GetStateAsync(It.IsAny())).ReturnsAsync(() => applicationState); var builder = Host.CreateDefaultBuilder() + .UseEnvironment("Development") .ConfigureServices((_, services) => { services.AddTransient>( diff --git a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs index 84e813b61..cfefde706 100644 --- a/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs +++ b/src/AppModel/NetDaemon.AppModel/Common/Extensions/ServiceCollectionExtension.cs @@ -122,6 +122,7 @@ private static IServiceCollection AddAppModelIfNotExist(this IServiceCollection .AddSingleton(s => s.GetRequiredService()) .AddTransient() .AddTransient(s => s.GetRequiredService()) + .AddTransient() .AddScopedConfigurationBinder() .AddScopedAppServices() .AddConfigManagement(); diff --git a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs index cd2b138ae..f6fe4938d 100644 --- a/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs +++ b/src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs @@ -1,5 +1,5 @@ +using System.Reflection; using NetDaemon.AppModel.Internal.AppFactoryProviders; -using IAppFactory = NetDaemon.AppModel.Internal.AppFactories.IAppFactory; namespace NetDaemon.AppModel.Internal; @@ -9,54 +9,39 @@ internal class AppModelContext : IAppModelContext, IAsyncInitializable private readonly IEnumerable _appFactoryProviders; private readonly IServiceProvider _provider; + private readonly FocusFilter _focusFilter; private bool _isDisposed; - public AppModelContext(IEnumerable appFactoryProviders, IServiceProvider provider) + public AppModelContext(IEnumerable appFactoryProviders, IServiceProvider provider, FocusFilter focusFilter) { _appFactoryProviders = appFactoryProviders; _provider = provider; + _focusFilter = focusFilter; } public IReadOnlyCollection Applications => _applications; - public async ValueTask DisposeAsync() - { - if (_isDisposed) - return; - - foreach (var appInstance in _applications) await appInstance.DisposeAsync().ConfigureAwait(false); - _applications.Clear(); - _isDisposed = true; - } - public async Task InitializeAsync(CancellationToken cancellationToken) { - await LoadApplications(); - } + var factories = _appFactoryProviders.SelectMany(provider => provider.GetAppFactories()).ToList(); - private async Task LoadApplications() - { - var factories = GetAppFactories().ToList(); - var loadOnlyFocusedApps = ShouldLoadOnlyFocusedApps(factories); + var filteredFactories = _focusFilter.FilterFocusApps(factories); - foreach (var factory in factories) + foreach (var factory in filteredFactories) { - 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, factory); await app.InitializeAsync().ConfigureAwait(false); _applications.Add(app); } } - private IEnumerable GetAppFactories() + public async ValueTask DisposeAsync() { - return _appFactoryProviders.SelectMany(provider => provider.GetAppFactories()); - } + if (_isDisposed) + return; - private static bool ShouldLoadOnlyFocusedApps(IEnumerable factories) - { - return factories.Any(factory => factory.HasFocus); + foreach (var appInstance in _applications) await appInstance.DisposeAsync().ConfigureAwait(false); + _applications.Clear(); + _isDisposed = true; } } \ No newline at end of file diff --git a/src/AppModel/NetDaemon.AppModel/Internal/FocusFilter.cs b/src/AppModel/NetDaemon.AppModel/Internal/FocusFilter.cs new file mode 100644 index 000000000..8cb776298 --- /dev/null +++ b/src/AppModel/NetDaemon.AppModel/Internal/FocusFilter.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Hosting; +using NetDaemon.AppModel.Internal.AppFactories; + +namespace NetDaemon.AppModel.Internal; + +class FocusFilter +{ + private readonly ILogger _logger; + private readonly IHostEnvironment _hostEnvironment; + + public FocusFilter(ILogger logger, IHostEnvironment hostEnvironment) + { + _logger = logger; + _hostEnvironment = hostEnvironment; + } + + public IReadOnlyCollection FilterFocusApps(IReadOnlyCollection allApps) + { + var focusApps = allApps.Where(a => a.HasFocus).ToList(); + + if (focusApps.Count == 0) return allApps; + + foreach (var focusApp in focusApps) + { + _logger.LogInformation("[Focus] attribute is set for app {AppName}", focusApp.Id); + } + + if (!_hostEnvironment.IsDevelopment()) + { + _logger.LogError("{Count} Focus apps were found but current environment is not 'Development', the [Focus] attribute is ignored" + + "Make sure the environment variable `DOTNET_ENVIRONMENT` is set to `Development` to use [Focus] or remove the [Focus] attribute when running in production", focusApps.Count); + return allApps; + } + + _logger.LogWarning($"Found {focusApps.Count} [Focus] apps, skipping all other apps"); + return focusApps; + } +} \ No newline at end of file