Add project files.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class AppServices : IHostedService
|
||||
{
|
||||
private readonly IMediator mediator;
|
||||
private readonly IDisposer disposer;
|
||||
private readonly IInitialization initialization;
|
||||
|
||||
public AppServices(IMediator mediator,
|
||||
IDisposer disposer,
|
||||
IInitialization initialization)
|
||||
{
|
||||
this.mediator = mediator;
|
||||
this.disposer = disposer;
|
||||
this.initialization = initialization;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
mediator.Handle<Started>();
|
||||
await initialization.InitializeAsync();
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
disposer.Dispose(this);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Disposer : IDisposer
|
||||
{
|
||||
private readonly ConditionalWeakTable<object, CompositeDisposable> subjects = new();
|
||||
|
||||
public void Add(object subject, params object[] objects)
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
foreach (IDisposable disposable in objects.OfType<IDisposable>())
|
||||
{
|
||||
disposables.Add(disposable);
|
||||
}
|
||||
|
||||
foreach (object notDisposable in objects.Where(x => x is not IDisposable))
|
||||
{
|
||||
disposables.Add(Disposable.Create(() => FromNotDisposable(notDisposable)));
|
||||
}
|
||||
}
|
||||
|
||||
private void FromNotDisposable(object target)
|
||||
{
|
||||
if (target is IEnumerable enumerableTarget)
|
||||
{
|
||||
foreach (object? item in enumerableTarget)
|
||||
{
|
||||
FromNotDisposable(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (target is IDisposable disposableTarget)
|
||||
{
|
||||
disposableTarget.Dispose();
|
||||
}
|
||||
|
||||
if (target is not IDisposable)
|
||||
{
|
||||
Dispose(target);
|
||||
}
|
||||
}
|
||||
|
||||
public TDisposable Replace<TDisposable>(object subject, IDisposable disposer, TDisposable replacement) where TDisposable : IDisposable
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
if (disposer is not null)
|
||||
{
|
||||
disposables.Remove(disposer);
|
||||
}
|
||||
|
||||
disposables.Add(replacement);
|
||||
return replacement;
|
||||
}
|
||||
|
||||
public void Remove(object subject, IDisposable disposer)
|
||||
{
|
||||
CompositeDisposable disposables = subjects.GetOrCreateValue(subject);
|
||||
if (disposer is not null)
|
||||
{
|
||||
disposables.Remove(disposer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose(object subject)
|
||||
{
|
||||
if (subjects.TryGetValue(subject, out CompositeDisposable? disposables))
|
||||
{
|
||||
disposables?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregator : IEventAggregator
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, object> subjects;
|
||||
|
||||
public EventAggregator(IEventAggregatorInvoker invoker)
|
||||
{
|
||||
subjects = new ConcurrentDictionary<Type, object>();
|
||||
Dispatcher = new SynchronizationContextScheduler(SynchronizationContext.Current!);
|
||||
Invoker = invoker;
|
||||
|
||||
Current = new EventAggregatorCurrent(this);
|
||||
}
|
||||
|
||||
protected EventAggregator(EventAggregator eventAggregator)
|
||||
{
|
||||
subjects = eventAggregator.subjects;
|
||||
Dispatcher = eventAggregator.Dispatcher;
|
||||
Invoker = eventAggregator.Invoker;
|
||||
Current = eventAggregator.Current;
|
||||
}
|
||||
|
||||
public IEventAggregator Current { get; }
|
||||
|
||||
public IScheduler Dispatcher { get; }
|
||||
|
||||
public IEventAggregatorInvoker Invoker { get; }
|
||||
|
||||
public virtual IObservable<TEvent> GetEvent<TEvent>()
|
||||
{
|
||||
return GetObservable<TEvent>().Skip(1);
|
||||
}
|
||||
|
||||
public virtual ISubject<TEvent> GetSubject<TEvent>()
|
||||
{
|
||||
return (ISubject<TEvent>)subjects.GetOrAdd(typeof(TEvent), x => new BehaviorSubject<TEvent>(default!));
|
||||
}
|
||||
|
||||
public virtual void Publish<TEvent>(TEvent domainEvent)
|
||||
{
|
||||
GetSubject<TEvent>().OnNext(domainEvent);
|
||||
}
|
||||
protected virtual IObservable<TEvent> GetObservable<TEvent>()
|
||||
{
|
||||
return GetSubject<TEvent>().AsObservable();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Reactive.Linq;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregatorCurrent : EventAggregator
|
||||
{
|
||||
public EventAggregatorCurrent(EventAggregator eventAggregator) : base(eventAggregator)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override IObservable<TEvent> GetEvent<TEvent>()
|
||||
{
|
||||
return GetObservable<TEvent>().Skip(0).Where(x => x != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class EventAggregatorInvoker : IEventAggregatorInvoker
|
||||
{
|
||||
private readonly IScope scope;
|
||||
|
||||
public EventAggregatorInvoker(IScope scope)
|
||||
{
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public void Invoke<TEvent>(object target, TEvent item, MethodInfo methodInfo)
|
||||
{
|
||||
using (scope.Enter<TEvent>(target))
|
||||
{
|
||||
methodInfo.Invoke(target, new object[] { item! });
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInvoking<TEvent>(object target)
|
||||
{
|
||||
return scope.IsActive<TEvent>(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface ICachable
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IDisposer
|
||||
{
|
||||
void Add(object subject, params object[] objects);
|
||||
|
||||
TDisposable Replace<TDisposable>(object subject, IDisposable disposer, TDisposable replacement) where TDisposable : IDisposable;
|
||||
|
||||
void Remove(object subject, IDisposable disposer);
|
||||
|
||||
void Dispose(object subject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventAggregator
|
||||
{
|
||||
IEventAggregator Current { get; }
|
||||
|
||||
IScheduler Dispatcher { get; }
|
||||
|
||||
IEventAggregatorInvoker Invoker { get; }
|
||||
|
||||
IObservable<TEvent> GetEvent<TEvent>();
|
||||
|
||||
ISubject<TEvent> GetSubject<TEvent>();
|
||||
|
||||
void Publish<TEvent>(TEvent args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IEventAggregatorInvoker
|
||||
{
|
||||
void Invoke<TEvent>(object target, TEvent item, MethodInfo methodInfo);
|
||||
|
||||
bool IsInvoking<TEvent>(object target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public interface IHasSensorPlacement
|
||||
{
|
||||
SensorPlacement Placement { get; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IInitialization
|
||||
{
|
||||
Task InitializeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IInitializer
|
||||
{
|
||||
Task InitializeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IKeepAlive
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IMediator
|
||||
{
|
||||
void Handle(object request, params object[] parameters);
|
||||
|
||||
TResponse? Handle<TResponse>(object request, params object[] parameters);
|
||||
|
||||
Task HandleAsync(object request, params object[] parameters);
|
||||
|
||||
Task HandleAsync(object request, CancellationToken cancellationToken, params object[] parameters);
|
||||
|
||||
Task<TResponse?> HandleAsync<TResponse>(object request, params object[] parameters);
|
||||
|
||||
Task<TResponse?> HandleAsync<TResponse>(object request, CancellationToken cancellationToken, params object[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IScope
|
||||
{
|
||||
IDisposable Enter<T>(object target);
|
||||
|
||||
bool IsActive<T>(object target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IServiceCreator<I>
|
||||
{
|
||||
object Create(Func<Type, object[], object> creator, params object[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public interface IServiceFactory
|
||||
{
|
||||
T? Get<T>(Type type);
|
||||
|
||||
T Create<T>(Type type, params object?[] parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public interface ITwoStateSensor
|
||||
{
|
||||
SensorState State { get; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Initialization : IInitialization
|
||||
{
|
||||
private readonly Func<IEnumerable<IInitializer?>> factory;
|
||||
|
||||
public Initialization(Func<IEnumerable<IInitializer?>> factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
foreach (IInitializer? initializer in factory())
|
||||
{
|
||||
if (initializer is not null)
|
||||
{
|
||||
await initializer.InitializeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public class Mediator : IMediator
|
||||
{
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public Mediator(IServiceFactory serviceFactory)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
public void Handle(object request, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorHandler<>).MakeGenericType(request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
handler.Handle((dynamic)request);
|
||||
}
|
||||
}
|
||||
|
||||
public TResponse? Handle<TResponse>(object request, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorHandler<,>).MakeGenericType(typeof(TResponse), request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task HandleAsync(object request, CancellationToken cancellationToken, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorAsyncHandler<>).MakeGenericType(request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task HandleAsync(object request, params object[] parameters)
|
||||
{
|
||||
return HandleAsync(request, CancellationToken.None, parameters);
|
||||
}
|
||||
|
||||
public Task<TResponse?> HandleAsync<TResponse>(object request, CancellationToken cancellationToken, params object[] parameters)
|
||||
{
|
||||
if (GetHandler(typeof(IMediatorAsyncHandler<,>).MakeGenericType(typeof(TResponse), request.GetType()), parameters) is { } handler)
|
||||
{
|
||||
return handler.Handle((dynamic)request, cancellationToken);
|
||||
}
|
||||
|
||||
return Task.FromResult<TResponse?>(default);
|
||||
}
|
||||
|
||||
public Task<TResponse?> HandleAsync<TResponse>(object request, params object[] parameters)
|
||||
{
|
||||
return HandleAsync<TResponse?>(request, CancellationToken.None, parameters);
|
||||
}
|
||||
|
||||
private dynamic? GetHandler(Type type, params object[] parameters)
|
||||
{
|
||||
return parameters.Length == 0 ? serviceFactory.Get<object>(type) : serviceFactory.Create<object>(type, parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive.Disposables;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class Scope : IScope
|
||||
{
|
||||
private readonly ConcurrentDictionary<object, bool> scopes = new ConcurrentDictionary<object, bool>();
|
||||
|
||||
public IDisposable Enter<T>(object target)
|
||||
{
|
||||
scopes.TryAdd(Tuple.Create(target, typeof(T)), true);
|
||||
return Disposable.Create(() => scopes.TryRemove(Tuple.Create(target, typeof(T)), out bool value));
|
||||
}
|
||||
|
||||
public bool IsActive<T>(object target)
|
||||
{
|
||||
return scopes.ContainsKey(Tuple.Create(target, typeof(T)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public enum SensorPlacement
|
||||
{
|
||||
Left = 0,
|
||||
Top = 1,
|
||||
Right = 3,
|
||||
Bottom = 4,
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public enum SensorState
|
||||
{
|
||||
Off = 0,
|
||||
On = 1
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public class ServiceCreator<I, T> : IServiceCreator<I>
|
||||
{
|
||||
public virtual object Create(Func<Type, object[], object> creator, params object[] parameters)
|
||||
{
|
||||
return creator(typeof(T), parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace TheXamlGuy.Framework.Core;
|
||||
|
||||
public class ServiceFactory : IServiceFactory
|
||||
{
|
||||
private readonly Func<Type, object?> factory;
|
||||
private readonly Func<Type, object?[], object> creator;
|
||||
|
||||
public ServiceFactory(Func<Type, object?> factory, Func<Type, object?[], object> creator)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public T? Get<T>(Type type)
|
||||
{
|
||||
T? value = (T?)factory(type);
|
||||
return value;
|
||||
}
|
||||
|
||||
public T Create<T>(Type type, params object?[] parameters)
|
||||
{
|
||||
dynamic? lookup = factory(typeof(IServiceCreator<>).MakeGenericType(type));
|
||||
return lookup is not null ? (T)lookup.Create(creator, parameters) : (T)creator(type, parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
internal class ServiceFactoryDescriptor
|
||||
{
|
||||
private readonly IServiceFactory serviceFactory;
|
||||
|
||||
public ServiceFactoryDescriptor(IServiceFactory serviceFactory)
|
||||
{
|
||||
this.serviceFactory = serviceFactory;
|
||||
}
|
||||
|
||||
public object? Create(Type type)
|
||||
{
|
||||
MethodInfo? methodInfo = typeof(ServiceFactoryDescriptor).GetMethod(nameof(Create), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
return methodInfo?.MakeGenericMethod(type).Invoke(this, new object[] { type });
|
||||
}
|
||||
|
||||
private T Create<T>(Type type)
|
||||
{
|
||||
return serviceFactory.Create<T>(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace TheXamlGuy.Framework.Core
|
||||
{
|
||||
public record Started;
|
||||
}
|
||||
Reference in New Issue
Block a user