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;
public class MediaControllerFactory(IMediator mediator,
IServiceScopeFactory<MediaController> serviceScopeFactory) :
public class MediaControllerFactory(IServiceScopeFactory<MediaController> serviceScopeFactory) :
IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>
{
public MediaController? Create(GlobalSystemMediaTransportControlsSession value)
@@ -11,22 +10,6 @@ public class MediaControllerFactory(IMediator mediator,
if (serviceScopeFactory.Create(value) is MediaController 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;
@@ -6,7 +6,7 @@ public class MediaControllerManager(IMediator mediator,
IFactory<GlobalSystemMediaTransportControlsSession, MediaController> factory) :
IInitializer
{
private readonly ConcurrentDictionary<GlobalSystemMediaTransportControlsSession, MediaController> cachedSessions = [];
private readonly List<KeyValuePair<GlobalSystemMediaTransportControlsSession, MediaController>> cachedSessions = [];
public async Task InitializeAsync()
{
@@ -28,7 +28,7 @@ public class MediaControllerManager(IMediator mediator,
if (factory.Create(session) is 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();
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)
{
await InitializeSessionAsync(session);
if (!cachedSessions.Any(x => x.Key.SourceAppUserModelId == session.SourceAppUserModelId))
{
await InitializeSessionAsync(session);
}
}
}
}
@@ -13,10 +13,11 @@ public class MediaControllerWidgetProvider :
.AddSingleton<IInitializer, MediaControllerManager>()
.AddTransient<IServiceScopeFactory<MediaController>, ServiceScopeFactory<MediaController>>()
.AddTransient<IServiceScopeProvider<MediaController>, ServiceScopeProvider<MediaController>>()
.AddSingleton<ConcurrentDictionary<MediaController, IServiceScope>>()
.AddCache<MediaController, IServiceScope>()
.AddTransient<IFactory<GlobalSystemMediaTransportControlsSession, MediaController?>, MediaControllerFactory>()
.AddHandler<MediaControllerHandler>()
.AddTransient<IFactory<MediaControllerViewModel?>, MediaControllerViewModelFactory>()
.AddCache<MediaControllerViewModel>()
.AddContentTemplate<MediaControllerViewModel, MediaControllerView>()
.AddContentTemplate<MediaInformationViewModel, MediaInformationView>();
}
@@ -8,7 +8,7 @@ public class PrimaryWidgetProvider :
{
public void Create(HostBuilderContext comtext, IServiceCollection services) =>
services.AddConfiguration<PrimaryWidgetConfiguration>()
.AddSingleton<ICache<Guid, IWidgetComponentViewModel>, Cache<Guid, IWidgetComponentViewModel>>()
.AddCache<Guid, IWidgetComponentViewModel>()
.AddTransient<IFactory<PrimaryCommandConfiguration, IWidgetComponentViewModel?>, WidgetComponentViewModelFactory>()
.AddTransient<IViewModelEnumerator<IWidgetComponentViewModel>, WidgetComponentViewModelEnumerator>()
.AddWidgetTemplate<PrimaryWidgetViewModel>()
+1
View File
@@ -38,6 +38,7 @@ public partial class App :
services.AddSingleton<IMediator, Mediator>();
services.AddSingleton<IDisposer, Disposer>();
services.AddSingleton<IDispatcher, Dispatcher>();
services.AddHostedService<AppService>();
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.AddTransient<ITemplateFactory, TemplateFactory>();
isolatedServices.AddScoped<IMediator, Mediator>();
isolatedServices.AddScoped<IDisposer, Disposer>();
isolatedServices.AddSingleton<IDispatcher, Dispatcher>();
isolatedServices.AddScoped<IVirtualKeyboard, VirtualKeyboard>();
@@ -9,22 +9,31 @@ namespace Hyperbar;
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)
where TConfiguration :
where TConfiguration :
class, new()
{
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,
TConfiguration? defaults = null)
where TConfiguration :
@@ -65,12 +74,12 @@ public static class IServiceCollectionExtensions
serializerDelegate.Invoke(defaultSerializer);
}
return new ConfigurationReader<TConfiguration>(provider.GetRequiredService<IConfigurationSource<TConfiguration>>(),
return new ConfigurationReader<TConfiguration>(provider.GetRequiredService<IConfigurationSource<TConfiguration>>(),
defaultSerializer);
});
services.AddSingleton<IConfigurationWriter<TConfiguration>>(provider =>
{
{
JsonSerializerOptions? defaultSerializer = null;
if (serializerDelegate is not null)
{
@@ -78,7 +87,7 @@ public static class IServiceCollectionExtensions
serializerDelegate.Invoke(defaultSerializer);
}
return new ConfigurationWriter<TConfiguration>(provider.GetRequiredService<IConfigurationSource<TConfiguration>>(),
return new ConfigurationWriter<TConfiguration>(provider.GetRequiredService<IConfigurationSource<TConfiguration>>(),
defaultSerializer);
});
@@ -115,7 +124,7 @@ public static class IServiceCollectionExtensions
}
public static IServiceCollection AddHandler<THandler>(this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Transient)
ServiceLifetime lifetime = ServiceLifetime.Transient)
where THandler :
IHandler
{
@@ -134,31 +143,40 @@ public static class IServiceCollectionExtensions
provider => provider.GetRequiredService<THandler>(), lifetime));
}
}
}
}
if (contract.Name == typeof(IHandler<,>).Name)
{
if (contract.GetGenericArguments() is { Length: 2 } arguments)
{
Type requestType = arguments[0];
Type responseType = arguments[1];
if (typeof(THandler).GetInterface(typeof(IHandler<,>).Name) is { } requestContract)
{
if (requestContract.GetGenericArguments() is { Length: 2 } arguments)
{
Type requestType = arguments[0];
Type responseType = arguments[1];
Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType);
Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType);
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.Add(new ServiceDescriptor(wrapperType,
provider => provider.GetService<IServiceFactory>()?.Create(wrapperType,
provider.GetRequiredService<THandler>(),
provider.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!,
lifetime
));
services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime));
services.Add(new ServiceDescriptor(wrapperType,
provider => provider.GetService<IServiceFactory>()?.Create(wrapperType,
provider.GetRequiredService<THandler>(),
provider.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!,
lifetime
));
}
}
}
}
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)
where TWidgetContent :
IWidgetViewModel
+36 -51
View File
@@ -1,83 +1,68 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Concurrent;
using System.Reactive.Disposables;
namespace Hyperbar;
public class Cache<TKey, TService>(IDisposer disposer) :
ICache<TKey, TService>
where TKey : notnull
public class Cache<TValue>(IDisposer disposer) :
ICache<TValue>
{
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];
set => cache[key] = value;
disposer.Add(value!, Disposable.Create(() =>
{
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(() =>
{
Remove(key);
}));
cache.Add(key, value);
}
public void Add(KeyValuePair<TKey, TService> item)
{
cache.Add(item);
cache.TryAdd(key, value);
}
public void Clear() => cache.Clear();
public bool Contains(KeyValuePair<TKey, TService> item)
{
return cache.Contains(item);
}
public bool ContainsKey(TKey key) => cache.ContainsKey(key);
public bool ContainsKey(TKey key)
{
return cache.ContainsKey(key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => cache.GetEnumerator();
public void CopyTo(KeyValuePair<TKey, TService>[] array, int arrayIndex)
{
cache.CopyTo(array, arrayIndex);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<KeyValuePair<TKey, TService>> GetEnumerator()
{
return cache.GetEnumerator();
}
public bool Remove(TKey key) => cache.Remove(key, out _);
public bool Remove(TKey key)
public bool TryGetValue(TKey key, out TValue? value)
{
return cache.Remove(key);
}
if (cache.TryGetValue(key, out value))
{
return true;
}
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();
value = default;
return false;
}
}
+24 -3
View File
@@ -1,8 +1,29 @@
namespace Hyperbar;
using Microsoft.Extensions.DependencyInjection;
public interface ICache<TKey, TViewModel> :
IDictionary<TKey, TViewModel>
namespace Hyperbar;
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
{
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 System.Collections.Concurrent;
namespace Hyperbar;
public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFactory,
ConcurrentDictionary<TService, IServiceScope> services) :
ICache<TService, IServiceScope> cache) :
IServiceScopeFactory<TService>
where TService : notnull
{
@@ -16,7 +15,7 @@ public class ServiceScopeFactory<TService>(IServiceScopeFactory serviceScopeFact
{
if (serviceFactory.Create<TService>(parameters) is TService service)
{
services.TryAdd(service, serviceScope);
cache.Add(service, serviceScope);
return service;
}
}
+2 -3
View File
@@ -1,16 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
namespace Hyperbar;
public class ServiceScopeProvider<TService>(ConcurrentDictionary<TService, IServiceScope> services) :
public class ServiceScopeProvider<TService>(ICache<TService, IServiceScope> cache) :
IServiceScopeProvider<TService>
where TService : notnull
{
public bool TryGet(TService service,
out IServiceScope? serviceScope)
{
if (services.TryGetValue(service, out IServiceScope? value))
if (cache.TryGetValue(service, out IServiceScope? value))
{
serviceScope = value;
return true;
+10 -5
View File
@@ -1,16 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using System.Reactive.Concurrency;
namespace Hyperbar;
public class Mediator(IServiceProvider provider) :
public interface IDispatcher
{
Task InvokeAsync(Action action);
}
public class Mediator(IServiceProvider provider,
IDispatcher dispatcher) :
IMediator
{
private readonly SynchronizationContext? context = SynchronizationContext.Current;
private readonly ConcurrentDictionary<Type, List<dynamic>> subjects = [];
public ValueTask PublishAsync<TNotification>(TNotification notification,
public async ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default)
where TNotification :
INotification
@@ -31,10 +38,8 @@ public class Mediator(IServiceProvider provider) :
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,
@@ -283,6 +283,7 @@ public partial class ObservableCollectionViewModel<TItem> :
return true;
}
void IList.Remove(object? value)
{
if (IsCompatibleObject(value))