add a central cache, for easy data retriveal

This commit is contained in:
TheXamlGuy
2024-01-14 20:35:38 +00:00
parent 1283e8ff58
commit 80f4d5a702
14 changed files with 154 additions and 122 deletions
@@ -2,8 +2,7 @@
namespace Hyperbar.Windows.MediaController; namespace Hyperbar.Windows.MediaController;
public class MediaControllerFactory(IMediator mediator, public class MediaControllerFactory(IServiceScopeFactory<MediaController> serviceScopeFactory) :
IServiceScopeFactory<MediaController> serviceScopeFactory) :
IFactory<GlobalSystemMediaTransportControlsSession, MediaController?> IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>
{ {
public MediaController? Create(GlobalSystemMediaTransportControlsSession value) public MediaController? Create(GlobalSystemMediaTransportControlsSession value)
@@ -11,22 +10,6 @@ public class MediaControllerFactory(IMediator mediator,
if (serviceScopeFactory.Create(value) is MediaController mediaController) if (serviceScopeFactory.Create(value) is MediaController mediaController)
{ {
return mediaController; return mediaController;
//if (serviceScope.ServiceProvider.GetService<IServiceFactory>() is IServiceFactory serviceFactory)
//{
// if (serviceFactory.Create<MediaController>(value) is MediaController mediaController)
// {
// //if (serviceScope.ServiceProvider.GetService<IFactory<MediaControllerViewModel?>>()
// // is IFactory<MediaControllerViewModel?> factory)
// //{
// // if (factory.Create() is MediaControllerViewModel mediaControllerViewModel)
// // {
// // _ = await mediator.PublishAsync(new Created<MediaControllerViewModel>(mediaControllerViewModel));
// // }
// //}
// }
//}
} }
return default; return default;
@@ -6,7 +6,7 @@ public class MediaControllerManager(IMediator mediator,
IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) : IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) :
IInitializer IInitializer
{ {
private readonly ConcurrentDictionary<GlobalSystemMediaTransportControlsSession, MediaController> cachedSessions = []; private readonly List<KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController>> cachedSessions = [];
public async Task InitializeAsync() public async Task InitializeAsync()
{ {
@@ -28,7 +28,7 @@ public class MediaControllerManager(IMediator mediator,
if (factory.Create(session) is MediaController mediaController) if (factory.Create(session) is MediaController mediaController)
{ {
await mediator.PublishAsync(new Created<MediaController>(mediaController)); await mediator.PublishAsync(new Created<MediaController>(mediaController));
cachedSessions.TryAdd(session, mediaController); cachedSessions.Add(new KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController>(session, mediaController));
} }
} }
@@ -39,17 +39,20 @@ public class MediaControllerManager(IMediator mediator,
sender.GetSessions(); sender.GetSessions();
foreach (KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController> session in foreach (KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController> session in
cachedSessions) cachedSessions.ToList())
{ {
if (!sessions.Contains(session.Key)) if (!sessions.Any(x => x.SourceAppUserModelId == session.Key.SourceAppUserModelId))
{ {
cachedSessions.TryRemove(session); cachedSessions.Remove(session);
} }
} }
foreach (GlobalSystemMediaTransportControlsSession session in sessions) foreach (GlobalSystemMediaTransportControlsSession session in sessions)
{
if (!cachedSessions.Any(x => x.Key.SourceAppUserModelId == session.SourceAppUserModelId))
{ {
await InitializeSessionAsync(session); await InitializeSessionAsync(session);
} }
} }
}
} }
@@ -13,10 +13,11 @@ public class MediaControllerWidgetProvider :
.AddSingleton<IInitializer, MediaControllerManager>() .AddSingleton<IInitializer, MediaControllerManager>()
.AddTransient<IServiceScopeFactory<MediaController>, ServiceScopeFactory<MediaController>>() .AddTransient<IServiceScopeFactory<MediaController>, ServiceScopeFactory<MediaController>>()
.AddTransient<IServiceScopeProvider<MediaController>, ServiceScopeProvider<MediaController>>() .AddTransient<IServiceScopeProvider<MediaController>, ServiceScopeProvider<MediaController>>()
.AddSingleton<ConcurrentDictionary<MediaController, IServiceScope>>() .AddCache<MediaController, IServiceScope>()
.AddTransient<IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>, MediaControllerFactory>() .AddTransient<IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>, MediaControllerFactory>()
.AddHandler<MediaControllerHandler>() .AddHandler<MediaControllerHandler>()
.AddTransient<IFactory<MediaControllerViewModel?>, MediaControllerViewModelFactory>() .AddTransient<IFactory<MediaControllerViewModel?>, MediaControllerViewModelFactory>()
.AddCache<MediaControllerViewModel>()
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>() .AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
.AddContentTemplate<MediaInformationViewModel, MediaInformationView>(); .AddContentTemplate<MediaInformationViewModel, MediaInformationView>();
} }
@@ -8,7 +8,7 @@ public class PrimaryWidgetProvider :
{ {
public void Create(HostBuilderContext comtext, IServiceCollection services) => public void Create(HostBuilderContext comtext, IServiceCollection services) =>
services.AddConfiguration<PrimaryWidgetConfiguration>() services.AddConfiguration<PrimaryWidgetConfiguration>()
.AddSingleton<ICache<Guid, IWidgetComponentViewModel>, Cache<Guid, IWidgetComponentViewModel>>() .AddCache<Guid, IWidgetComponentViewModel>()
.AddTransient<IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>() .AddTransient<IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>() .AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
.AddWidgetTemplate<PrimaryWidgetViewModel>() .AddWidgetTemplate<PrimaryWidgetViewModel>()
+1
View File
@@ -38,6 +38,7 @@ public partial class App :
services.AddSingleton<IMediator, Mediator>(); services.AddSingleton<IMediator, Mediator>();
services.AddSingleton<IDisposer, Disposer>(); services.AddSingleton<IDisposer, Disposer>();
services.AddSingleton<IDispatcher, Dispatcher>();
services.AddHostedService<AppService>(); services.AddHostedService<AppService>();
services.AddConfiguration<AppConfiguration>(); services.AddConfiguration<AppConfiguration>();
+14
View File
@@ -0,0 +1,14 @@
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
namespace Hyperbar.Windows;
public class Dispatcher :
IDispatcher
{
private DispatcherQueue dispatcherQueue;
public Dispatcher() => dispatcherQueue = DispatcherQueue.GetForCurrentThread();
public async Task InvokeAsync(Action action) => await dispatcherQueue.EnqueueAsync(action.Invoke);
}
@@ -34,8 +34,10 @@ namespace Hyperbar.Windows
isolatedServices.AddHostedService<WidgetService>(); isolatedServices.AddHostedService<WidgetService>();
isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>(); isolatedServices.AddTransient<ITemplateFactory, TemplateFactory>();
isolatedServices.AddScoped<IMediator, Mediator>(); isolatedServices.AddScoped<IMediator, Mediator>();
isolatedServices.AddScoped<IDisposer, Disposer>(); isolatedServices.AddScoped<IDisposer, Disposer>();
isolatedServices.AddSingleton<IDispatcher, Dispatcher>();
isolatedServices.AddScoped<IVirtualKeyboard, VirtualKeyboard>(); isolatedServices.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
@@ -9,6 +9,24 @@ namespace Hyperbar;
public static class IServiceCollectionExtensions public static class IServiceCollectionExtensions
{ {
public static IServiceCollection AddCache<TKey, TValue>(this IServiceCollection services)
where TKey :
notnull
{
services.AddSingleton<ICache<TKey, TValue>, Cache<TKey, TValue>>();
services.AddTransient(provider => provider.GetService<ICache<TKey, TValue>>()!.Select(x => x.Value));
return services;
}
public static IServiceCollection AddCache<TValue>(this IServiceCollection services)
{
services.AddSingleton<ICache<TValue>, Cache<TValue>>();
services.AddTransient(provider => provider.GetService<ICache<TValue>>()!.Select(x => x));
return services;
}
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services) public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services)
where TConfiguration : where TConfiguration :
class, new() class, new()
@@ -16,15 +34,6 @@ public static class IServiceCollectionExtensions
return services.AddConfiguration<TConfiguration>(typeof(TConfiguration).Name, "Settings.json", null); return services.AddConfiguration<TConfiguration>(typeof(TConfiguration).Name, "Settings.json", null);
} }
public static IServiceCollection AddNotificationPipeline<TFromNotification, TToNotification>(this IServiceCollection services)
where TFromNotification :
INotification
where TToNotification :
INotification, new()
{
return services.AddHandler<NotficationPipelineHandler<TFromNotification, TToNotification>>();
}
public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services, public static IServiceCollection AddConfiguration<TConfiguration>(this IServiceCollection services,
TConfiguration? defaults = null) TConfiguration? defaults = null)
where TConfiguration : where TConfiguration :
@@ -134,13 +143,10 @@ public static class IServiceCollectionExtensions
provider => provider.GetRequiredService<THandler>(), lifetime)); provider => provider.GetRequiredService<THandler>(), lifetime));
} }
} }
}
} if (contract.Name == typeof(IHandler<,>).Name)
if (typeof(THandler).GetInterface(typeof(IHandler<,>).Name) is { } requestContract)
{ {
if (requestContract.GetGenericArguments() is { Length: 2 } arguments) if (contract.GetGenericArguments() is { Length: 2 } arguments)
{ {
Type requestType = arguments[0]; Type requestType = arguments[0];
Type responseType = arguments[1]; Type responseType = arguments[1];
@@ -156,9 +162,21 @@ public static class IServiceCollectionExtensions
)); ));
} }
} }
}
}
return services; return services;
} }
public static IServiceCollection AddNotificationPipeline<TFromNotification, TToNotification>(this IServiceCollection services)
where TFromNotification :
INotification
where TToNotification :
INotification, new()
{
return services.AddHandler<NotficationPipelineHandler<TFromNotification, TToNotification>>();
}
public static IServiceCollection AddWidgetTemplate<TWidgetContent>(this IServiceCollection services) public static IServiceCollection AddWidgetTemplate<TWidgetContent>(this IServiceCollection services)
where TWidgetContent : where TWidgetContent :
IWidgetViewModel IWidgetViewModel
+39 -54
View File
@@ -1,83 +1,68 @@
using System.Collections; using System.Collections;
using System.Diagnostics.CodeAnalysis; using System.Collections.Concurrent;
using System.Reactive.Disposables; using System.Reactive.Disposables;
namespace Hyperbar; namespace Hyperbar;
public class Cache<TKey, TService>(IDisposer disposer) : public class Cache<TValue>(IDisposer disposer) :
ICache<TKey, TService> ICache<TValue>
where TKey : notnull
{ {
private readonly IDictionary<TKey, TService> cache = new Dictionary<TKey, TService>(); private readonly List<TValue> cache = [];
public TService this[TKey key] public void Add(TValue value)
{ {
get => cache[key]; disposer.Add(value!, Disposable.Create(() =>
set => cache[key] = value; {
Remove(value);
}));
cache.Add(value);
} }
public ICollection<TKey> Keys => cache.Keys; public void Clear() => cache.Clear();
public ICollection<TService> Values => cache.Values; public IEnumerator<TValue> GetEnumerator() => cache.GetEnumerator();
public int Count => cache.Count; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool IsReadOnly => false; public bool Remove(TValue value) => cache.Remove(value);
}
public void Add(TKey key, TService value) public class Cache<TKey, TValue>(IDisposer disposer) :
ICache<TKey, TValue>
where TKey : notnull
{
private readonly ConcurrentDictionary<TKey, TValue> cache = new();
public void Add(TKey key,
TValue value)
{ {
disposer.Add(value!, Disposable.Create(() => disposer.Add(value!, Disposable.Create(() =>
{ {
Remove(key); Remove(key);
})); }));
cache.Add(key, value); cache.TryAdd(key, value);
}
public void Add(KeyValuePair<TKey, TService> item)
{
cache.Add(item);
} }
public void Clear() => cache.Clear(); public void Clear() => cache.Clear();
public bool Contains(KeyValuePair<TKey, TService> item) public bool ContainsKey(TKey key) => cache.ContainsKey(key);
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => cache.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public bool Remove(TKey key) => cache.Remove(key, out _);
public bool TryGetValue(TKey key, out TValue? value)
{ {
return cache.Contains(item); if (cache.TryGetValue(key, out value))
{
return true;
} }
public bool ContainsKey(TKey key) value = default;
{ return false;
return cache.ContainsKey(key);
}
public void CopyTo(KeyValuePair<TKey, TService>[] array, int arrayIndex)
{
cache.CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TService>> GetEnumerator()
{
return cache.GetEnumerator();
}
public bool Remove(TKey key)
{
return cache.Remove(key);
}
public bool Remove(KeyValuePair<TKey, TService> item)
{
return cache.Remove(item);
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TService value)
{
return cache.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return cache.GetEnumerator();
} }
} }
+24 -3
View File
@@ -1,8 +1,29 @@
namespace Hyperbar; using Microsoft.Extensions.DependencyInjection;
public interface ICache<TKey, TViewModel> : namespace Hyperbar;
IDictionary<TKey, TViewModel>
public interface ICache<TValue> :
IEnumerable<TValue>
{
void Add(TValue value);
void Clear();
bool Remove(TValue value);
}
public interface ICache<TKey, TValue> :
IEnumerable<KeyValuePair<TKey, TValue>>
where TKey : notnull where TKey : notnull
{ {
void Add(TKey key,
TValue value);
void Clear();
bool ContainsKey(TKey key);
bool Remove(TKey key);
bool TryGetValue(TKey key, out TValue? value);
} }
+2 -3
View File
@@ -1,10 +1,9 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
namespace Hyperbar; namespace Hyperbar;
public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFactory, public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFactory,
ConcurrentDictionary<TService, IServiceScope> services) : ICache<TService, IServiceScope> cache) :
IServiceScopeFactory<TService> IServiceScopeFactory<TService>
where TService : notnull where TService : notnull
{ {
@@ -16,7 +15,7 @@ public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFact
{ {
if (serviceFactory.Create<TService>(parameters) is TService service) if (serviceFactory.Create<TService>(parameters) is TService service)
{ {
services.TryAdd(service, serviceScope); cache.Add(service, serviceScope);
return service; return service;
} }
} }
+2 -3
View File
@@ -1,16 +1,15 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
namespace Hyperbar; namespace Hyperbar;
public class ServiceScopeProvider<TService>(ConcurrentDictionary<TService, IServiceScope> services) : public class ServiceScopeProvider<TService>(ICache<TService, IServiceScope> cache) :
IServiceScopeProvider<TService> IServiceScopeProvider<TService>
where TService : notnull where TService : notnull
{ {
public bool TryGet(TService service, public bool TryGet(TService service,
out IServiceScope? serviceScope) out IServiceScope? serviceScope)
{ {
if (services.TryGetValue(service, out IServiceScope? value)) if (cache.TryGetValue(service, out IServiceScope? value))
{ {
serviceScope = value; serviceScope = value;
return true; return true;
+10 -5
View File
@@ -1,16 +1,23 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reactive.Concurrency;
namespace Hyperbar; namespace Hyperbar;
public class Mediator(IServiceProvider provider) : public interface IDispatcher
{
Task InvokeAsync(Action action);
}
public class Mediator(IServiceProvider provider,
IDispatcher dispatcher) :
IMediator IMediator
{ {
private readonly SynchronizationContext? context = SynchronizationContext.Current; private readonly SynchronizationContext? context = SynchronizationContext.Current;
private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = []; private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = [];
public ValueTask PublishAsync<TNotification>(TNotification notification, public async ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
where TNotification : where TNotification :
INotification INotification
@@ -31,10 +38,8 @@ public class Mediator(IServiceProvider provider) :
foreach (INotificationHandler<TNotification> handler in handlers) foreach (INotificationHandler<TNotification> handler in handlers)
{ {
context?.Post(async state => await handler.Handle(notification, cancellationToken), null); await dispatcher.InvokeAsync(async () => await handler.Handle(notification, cancellationToken));
} }
return ValueTask.CompletedTask;
} }
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> request, public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> request,
@@ -283,6 +283,7 @@ public partial class ObservableCollectionViewModel<TItem> :
return true; return true;
} }
void IList.Remove(object? value) void IList.Remove(object? value)
{ {
if (IsCompatibleObject(value)) if (IsCompatibleObject(value))