Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/AppModel/NetDaemon.AppModel.Tests/AppModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public async Task TestFocusShouldAlwaysLoadAppIfIndependentOfStateManager(Applic
fakeAppStateManager.Setup(n => n.GetStateAsync(It.IsAny<string>())).ReturnsAsync(() => applicationState);

var builder = Host.CreateDefaultBuilder()
.UseEnvironment("Development")
.ConfigureServices((_, services) =>
{
services.AddTransient<IOptions<AppConfigurationLocationSetting>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ private static IServiceCollection AddAppModelIfNotExist(this IServiceCollection
.AddSingleton<IAppModel>(s => s.GetRequiredService<AppModelImpl>())
.AddTransient<AppModelContext>()
.AddTransient<IAppModelContext>(s => s.GetRequiredService<AppModelContext>())
.AddTransient<FocusFilter>()
.AddScopedConfigurationBinder()
.AddScopedAppServices()
.AddConfigManagement();
Expand Down
41 changes: 13 additions & 28 deletions src/AppModel/NetDaemon.AppModel/Internal/AppModelContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Reflection;
using NetDaemon.AppModel.Internal.AppFactoryProviders;
using IAppFactory = NetDaemon.AppModel.Internal.AppFactories.IAppFactory;

namespace NetDaemon.AppModel.Internal;

Expand All @@ -9,54 +9,39 @@ internal class AppModelContext : IAppModelContext, IAsyncInitializable

private readonly IEnumerable<IAppFactoryProvider> _appFactoryProviders;
private readonly IServiceProvider _provider;
private readonly FocusFilter _focusFilter;
private bool _isDisposed;

public AppModelContext(IEnumerable<IAppFactoryProvider> appFactoryProviders, IServiceProvider provider)
public AppModelContext(IEnumerable<IAppFactoryProvider> appFactoryProviders, IServiceProvider provider, FocusFilter focusFilter)
{
_appFactoryProviders = appFactoryProviders;
_provider = provider;
_focusFilter = focusFilter;
}

public IReadOnlyCollection<IApplication> 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<Application>(_provider, factory);
await app.InitializeAsync().ConfigureAwait(false);
_applications.Add(app);
}
}

private IEnumerable<IAppFactory> GetAppFactories()
public async ValueTask DisposeAsync()
{
return _appFactoryProviders.SelectMany(provider => provider.GetAppFactories());
}
if (_isDisposed)
return;

private static bool ShouldLoadOnlyFocusedApps(IEnumerable<IAppFactory> factories)
{
return factories.Any(factory => factory.HasFocus);
foreach (var appInstance in _applications) await appInstance.DisposeAsync().ConfigureAwait(false);
_applications.Clear();
_isDisposed = true;
}
}
38 changes: 38 additions & 0 deletions src/AppModel/NetDaemon.AppModel/Internal/FocusFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.Extensions.Hosting;
using NetDaemon.AppModel.Internal.AppFactories;

namespace NetDaemon.AppModel.Internal;

class FocusFilter
{
private readonly ILogger<FocusFilter> _logger;
private readonly IHostEnvironment _hostEnvironment;

public FocusFilter(ILogger<FocusFilter> logger, IHostEnvironment hostEnvironment)
{
_logger = logger;
_hostEnvironment = hostEnvironment;
}

public IReadOnlyCollection<IAppFactory> FilterFocusApps(IReadOnlyCollection<IAppFactory> 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;
}
}