using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using static System.Console;
using SRM=System.Reflection.MethodBase;
using CMN=System.Runtime.CompilerServices.CallerMemberNameAttribute;
using CFP=System.Runtime.CompilerServices.CallerFilePathAttribute;
using CLN=System.Runtime.CompilerServices.CallerLineNumberAttribute;
using DN=System.Diagnostics.CodeAnalysis.DisallowNullAttribute;
using O=System.Runtime.InteropServices.OptionalAttribute;
using DPV=System.Runtime.InteropServices.DefaultParameterValueAttribute;
using dt=System.DateTime;
using Core.Delegates.Security;
using Core.Dependencies.Interfaces;
using Core.ErrorHandling.Exceptions;
using Core.Interfaces.Domain;
using Core.Interfaces.Misc;
using Core.Interfaces.Process;
using Core.Interfaces.Process.Measures;
using Core.Interfaces.Process.Processors;
using Core.Interfaces.Process.Results;
using Core.Interfaces.Security;
using Core.Interfaces.Services;
using Core.Process.Definitions;
using Core.Process.Measures;
using Core.Process.Processors;
using Core.Services.Business;
using Core.Services.Ingress;
using Core.Services.Providers;
using Core.Services.Security;
using static System.MemoryExtensions;
using static Core.Process.Processors.ManagedProcessExtensions;
using static DebugMethods;
using static UtilityMethods;
using IdType=System.Int32;
using IdentityType=System.String;
using TUnitOfWorkLog=Core.Process.Logs.UnitOfWorkLog;
public static Data<Person> People=new();
public static Data<Employee> Employees=new();
public class Data<T> : List<T>;
public static class Globals
public static DependencyInjection DI =>s_globalDI.Value;
public static InversionOfControl IoC =>s_globalIoC.Value;
private static Lazy<DependencyInjection> s_globalDI =new Lazy<DependencyInjection>( ()=>new DependencyInjection() );
private static Lazy<InversionOfControl> s_globalIoC =new Lazy<InversionOfControl>( ()=>new InversionOfControl() );
Globals.DI.AddService<ISecurityAuthorizableService>( ()=> new SecurityAuthorizationService( new SecurityIdentityRetrievalDelegate( ()=>"Injected") ) );
Globals.DI.AddService<IIngressConsumerService<Person>, IngressService<Person>>();
Globals.DI.AddService<IIngressConsumerService<Employee>, IngressService<Employee>>();
Globals.DI.AddService<IIngressProducerService<PersonId, Person>, IngressService<PersonId, Person>>();
Globals.DI.AddService<IIngressProducerService<EmployeeId, Employee>, IngressService<EmployeeId, Employee>>();
Globals.DI.AddService<IBusinessService<Person>, BusinessService<Person>>();
Globals.DI.AddService<IBusinessService<Employee>, BusinessService<Employee>>();
Globals.DI.AddService<IServiceProvider<Person>, SqlProvider<Person>>();
Globals.DI.AddService<IServiceProvider<Employee>, RdbmsProvider<Employee>>();
Globals.DI.AddService<ICrudProvider<Person>, SqlPersonProvider>();
Globals.DI.AddService<ICrudProvider<Employee>, SqlEmployeeProvider>();
public static Data Data=new();
public class DelegateInjection
public void AddDelegate<TDelegate, TServiceType, TImplementationType>()
where TDelegate : System.Delegate
public static void Main()
string TypeResolver(string typeString)
typeString=typeString.Replace(",","|");
string result=string.Empty;
var span=typeString.AsSpan();
int start=span.LastIndexOf('<');
int len=span.Length-start;
WL("span:\n" + span.ToString());
WL("012345678901234567890123456789");
var searchSpace=span.Slice(start, len);
WL("searchSpace:\n" + searchSpace.ToString());
var locEndBracket=searchSpace.IndexOf('>');
WL("locEndBracket:" + locEndBracket);
len=searchSpace.IndexOf('>');
var genericSpanWOBrackets=searchSpace.Slice(1,len-1);
WL("genericSpanWOBrackets:" + genericSpanWOBrackets.ToString());
var types=genericSpanWOBrackets.ToString().Split('|');
var numberOfTypes=types.Length;
WL("# types:" + numberOfTypes);
string replacementOldValue=searchSpace.ToString();
string replacementNewValue="`" + numberOfTypes + replacementOldValue.Replace("<","[").Replace(">","]");;
typeString=typeString.Replace(replacementOldValue, replacementNewValue);
span=typeString.AsSpan();
start=span.LastIndexOf('<');
WL("Result:" + typeString);
Hi(typeof(IIngressProducerService<PersonId, Person>));
Hi(Type.GetType("Core.Services.Ingress.IIngressProducerService`2[Core.Domain.PersonId, Core.Domain.Person]", false, true));
Hi(Type.GetType("Core.Services.Ingress.IngressService`1[Core.Domain.Person]", false, true));
Employees r=[new Employee(),new Employee()];
Dictionary<string, int> wordCounts = new(StringComparer.OrdinalIgnoreCase);
Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> spanLookup =
wordCounts.GetAlternateLookup<ReadOnlySpan<char>>();
var pConsumer=Globals.DI.GetService<IIngressConsumerService<Person>>();
pConsumer=Globals.DI.GetService<IIngressConsumerService<Person>>(Globals.DI.GetService<IBusinessService<Person>>(), Globals.DI.GetService<ISecurityAuthorizableService>() , "test");
var eConsumer=Globals.DI.GetService<IIngressConsumerService<Employee>>();
var eProducer=Globals.DI.GetService<IIngressProducerService<EmployeeId, Employee>>();
WL("+++++++++++++++++++++++++++++++++");
var er=eProducer.Get(e=>e.First=="f");
Hi(((IStateManaged)e).State.IsNew);
((IStateManaged)e).State.SetDeleted();
er=eProducer.Get(e=>e.First=="tug");
er=eProducer.Get(e=>e.Last=="McG");
namespace Core.Interfaces.Domain
public interface IEntityId : IEntityId<IdType>
public static IdType Create()
Core.IdManager.GetId(ref id);
public static IdType NewId => Create();
public interface IEntityId<T>
public static IEntityId<T> Empty => new EntityId<T?>(default);
public interface IEntityObjectId : IEntityObjectId<EntityId<IdType>>;
public interface IEntityObjectId<TEntityId>
where TEntityId : IEntityId<IdType>
public TEntityId EntityId { get; }
public void SetId(TEntityId id);
public static TEntityId? Empty => (TEntityId?)System.Activator.CreateInstance(typeof(TEntityId), [default]);
public interface zIDomainObjectModificationEvents
public virtual event DomainObjectEventHandler ItemRemoved
add => ItemRemoved += value;
remove => ItemRemoved -= value;
public virtual event DomainObjectEventHandler ItemChanged
add => ItemChanged += value;
remove => ItemChanged -= value;
public interface IStateful
public virtual bool IsActionable => IsNew || IsDirty || IsDeleted;
public virtual CrudOperation ToCrudOperation() => IsNew ? CrudOperation.Insert :
IsDirty ? CrudOperation.Update :
IsDeleted ? CrudOperation.Delete : CrudOperation.None;
public virtual CrudOperation Operation { get => ToCrudOperation(); }
public virtual string? ToString() =>$"IStateful: (IsNew: {IsNew}, IsDirty: {IsDirty}, IsDeleted: {IsDeleted})";
public interface IStateManager : IStateful
public event DomainObjectEventHandler ItemRemoved;
public event DomainObjectEventHandler ItemChanged;
public virtual void SetDirty() => SetDirty(true);
void SetDirty(bool isDirty);
public interface IStateManaged
public event DomainObjectEventHandler ItemRemoved;
public event DomainObjectEventHandler ItemChanged;
public IStateManager State { get; }
public interface ICreateable
public virtual bool IsCreatable => false;
public interface IActionable
public virtual bool IsActionable => true;
public virtual bool IsCreatable => false;
public interface IDomainObject : IDomainObject<IDomainObject>;
public interface IDomainObject<T> : IStateManaged, IActionable, IEmptyObject<T>
public interface IDomainAggregate<T> : IStateful, IEmptyAggregateObject<T>, ITransformableAggregate<T>, IEnumerable<T>, IEnumerable
public new IList<T> Items { get; }
bool IStateful.IsNew => this.Any(i=>i.State.IsNew);
bool IStateful.IsDirty => this.Any(i=>i.State.IsDirty);
bool IStateful.IsDeleted => this.Any(i=>i.State.IsDeleted);
public virtual void Add(T item )
item.ItemRemoved+=OnItemRemoved;
public virtual void AddItems(params ReadOnlySpan<T> items) => items.For(item=>Add(item));
if(!Enumerable.TryGetNonEnumeratedCount(Items, out int count)) return Items.Count();
public new virtual bool IsActionable => IsNew || IsDirty || IsDeleted;
public new IEnumerator<T> GetEnumerator() => Items.GetEnumerator();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => Items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Items.GetEnumerator();
public virtual T this[int index] => Items[index];
public Span<T> AsSpan() => new Span<T>(Items.ToArray());
public ReadOnlySpan<T> AsReadOnlySpan() => new ReadOnlySpan<T>(Items.ToArray());
public virtual void OnItemRemoved(object sender)
WL($"IDomainAggregate.OnItemRemoved: {item.ToString()}");
if(item.State.IsNew) Items.Remove( item );
public interface ITransformableAggregate<T>
public IList<T> Items { get; }
public virtual IEnumerable<TResult> Transform<TResult>([DN] Func<T, TResult> transformer)
var e=Items.GetEnumerator();
yield return transformer(e.Current);
public interface ICreate<T> { public T Add(T input); }
public interface ICreate<T, out TResult>{ public TResult Add(T input); }
public interface IUpdate<T> { public void Update(T input); }
public interface IDelete<T> { public void Delete(T input); }
public interface IRead<TResult>
where TResult : IStateManaged
public TResult Read(IdType id);
public IDomainAggregate<TResult> Read(Expression<Predicate<TResult>> predicate);
public interface IReadAggregate<T,out TAggregate>
where TAggregate : IDomainAggregate<T>
public TAggregate Read(Expression<Predicate<T>> predicate);
namespace Core.Interfaces.Misc
public interface IEmptyObject<T>
{ public static T? Empty = default(T); }
public interface IEmptyAggregateObject<T>
{ public static IEnumerable<T> Empty = Enumerable.Empty<T>(); }
{ public virtual object Id => "{GetType().DeclaringType.Name}<{t.GetType().Name}>"; }
public interface INew<T> { public T Create(); }
namespace Core.Interfaces.Process{}
namespace Core.Interfaces.Process.Measures
public interface ITimeable
public long start { get;set; }
public TimeSpan Duration { get;set; }
public void Start() => start=Stopwatch.GetTimestamp();
public TimeSpan Finish() => Duration=Stopwatch.GetElapsedTime(start);
namespace Core.Interfaces.Process.Processors
public interface IAsyncTaskProcessor
Task InvokeAsync([DN] Expression<Action> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync([DN] Action method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync<T>([DN] T input, [DN] Expression<Action<T>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync<T>([DN] ref readonly T input, [DN] Expression<Action<T>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task InvokeAsync<T>([DN] ref readonly T input, [DN] Action<T> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task InvokeAsync<T>([DN] T input, [DN] Action<T> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync([DN] Expression<Func<Task>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync([DN] Func<Task> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<TResult>([DN] Expression<Func<TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<TResult>([DN] Func<TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Expression<Func<T, TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Func<T, TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync<T>([DN] T input, [DN] Expression<Func<T, Task>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task InvokeAsync<T>([DN] ref readonly T input, [DN] Expression<Func<T, Task>> x, [Optional] AsyncOptions @options,[CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task InvokeAsync<T>([DN] ref readonly T input, [DN] Func<T, Task> method, [Optional] AsyncOptions @options,[CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task InvokeAsync<T>([DN] T input, [DN] Func<T, Task> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult> InvokeAsync<TResult>([DN] Expression<Func<Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult> InvokeAsync<TResult>([DN] Func<Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Expression<Func<T, Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Func<T, Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Func<T, Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
public interface ISyncProcessor
void Invoke([DN] Expression<Action> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0) ;
void Invoke([DN] Action method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
void Invoke<T>([DN] T input, [DN] Expression<Action<T>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
void Invoke<T>([DN] ref readonly T input, [DN] Expression<Action<T>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
void Invoke<T>([DN] ref readonly T input, [DN] Action<T> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
void Invoke<T>([DN] T input, [DN] Action<T> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
TResult? Invoke<TResult>([DN] Expression<Func<TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
TResult? Invoke<TResult>([DN] Func<TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
TResult? Invoke<T, TResult>([DN] T input, [DN] Expression<Func<T, TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
TResult? Invoke<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? Invoke<T, TResult>([DN] ref readonly T input, [DN] Func<T, TResult> producer, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? Invoke<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0);
public interface IProcessors : ISyncProcessor, IAsyncTaskProcessor
private static Processor allProcessors { get; } = new();
public virtual Processor processor => allProcessors.processor;
public interface IProcessors<TDerived> : IProcessors, ITypeNameable<TDerived>;
namespace Core.Interfaces.Process.Results
public interface IResultant
public UnitOfWorkLog Log { get;set; }
public interface IResultant<TResult> : IResultant
public TResult? Value { get;set; }
namespace Core.Interfaces.Security
public interface ISecurityToken
public IdentityType? Identity { get; }
public DateTime SessionStart { get; }
public DateTime SessionExpiration { get; }
public virtual TimeSpan SessionDuration { get=>dt.UtcNow-SessionStart; }
public virtual bool IsExpired { get=>dt.UtcNow>SessionExpiration; }
public IEnumerable<ProcessInfo> AllowedActions { get; }
public IEnumerable<string> Errors { get; }
public virtual bool CanAccess(ProcessInfo process) => AllowedActions.Contains(process);
public void AddError(string message);
public interface ISecurityAuthenticatableService
public ISecurityToken? Token { get; }
public bool TryAuthenticate(IdentityType identity, out ISecurityToken? token);
public interface ISecurityAuthorizableService : ISecurityAuthenticatableService
public bool TryAuthorize(ProcessInfo processInfo);
namespace Core.Interfaces.Services
public interface ITypeNameable
private protected static string? _typeName { get;set; }
protected virtual string TypeName => _typeName??=GetType().ToFriendlyName();
public interface ITypeNameable<TDerived> : ITypeNameable
protected virtual new string TypeName => _typeName??=typeof(TDerived).ToFriendlyName();
public interface IServiceType : ITypeNameable;
public interface IServiceType<TDerived> : ITypeNameable<TDerived>;
public interface ICommand : ICommand<IDomainObject>;
public interface ICommand<T> : IId
public void Process(T input);
public virtual TResult Process<TResult>(T input) => IEmptyObject<TResult>.Empty!;
public void ProcessParallel(T input){}
public void Save(T input)=>Process(input);
public interface ICommandAsync : ICommandAsync<IDomainObject>;
public interface ICommandAsync<T>
public Task ProcessAsync(T input);
public Task<TResult> ProcessAsync<TResult>(T input);
public interface IQuery : IQuery<IDomainObject, IEntityId>;
public interface IQuery<T> : IQuery<IEntityId, T>;
public interface IQuery<TEntityId, T> : IId
public T? Get(TEntityId id);
public IEnumerable<T> Get(Expression<Predicate<T>> predicate);
public interface IQueryAsync<in TEntityId, T> : IId
public Task<T?> GetAsync(TEntityId id);
public Task RunAsync(TEntityId id);
public interface ICommandQuery<T> : IId { public T Process(T input); }
public interface ICommandQuery<T, out TResult> : IId { public TResult Process(T input); }
public interface ISaver<T> { public void Save(T input); }
public interface IWriter<T> { public void Write(T input); }
public interface IReader<in TEntityId, T> : IId
where TEntityId : IEntityId
public T? Read(TEntityId id) => throw new NotImplementedException("Refer to the interface definition.");
public IEnumerable<T> Read(Expression<Predicate<T>> predicate) => throw new NotImplementedException("Refer to the interface definition.");
public interface ICrudProvider<T, TResult> : ICreate<T, TResult>, IUpdate<T>, IDelete<T>, IRead<TResult>
where TResult : IStateManaged;
public interface ICrudProvider<T> : ICreate<T>, IUpdate<T>, IDelete<T>, IRead<T>
public delegate IdentityType SecurityIdentityRetrievalDelegate();
namespace Core.Process.Definitions
public record struct ProcessInfo(string TypeName, string MethodName);
public readonly ref struct CallerInfo(string memberName, string filePath, int lineNumber)
public readonly string MemberName = memberName;
public readonly string FilePath = filePath;
public readonly int LineNumber = lineNumber;
public override string ToString() => string.Concat($"[Name:{MemberName}",
LineNumber !=0 ? $", Line:{LineNumber}" : "",
FilePath is not null && FilePath!="" ? $", Path:{FilePath}" : "",
namespace Core.Process.Measures
public class TimedProcess : ITimeable
public TimedProcess() => ( timer, start ) = ( (ITimeable)this, Stopwatch.GetTimestamp() );
public long start { get;set; }
public TimeSpan Duration { get;set; }
internal void Start() => timer.Start();
internal TimeSpan Finish() => timer.Finish();
namespace Core.Process.Processors
public record class AsyncOptions(ConfigureAwaitOptions ConfigureAwaitOptions = ConfigureAwaitOptions.None,
TaskCreationOptions CreationOptions = TaskCreationOptions.None,
TaskContinuationOptions ContinuationOptions = TaskContinuationOptions.AttachedToParent,
CancellationToken CancellationToken = default,
public AsyncOptions(bool ConfigureAwait = false,
TaskCreationOptions CreationOptions = TaskCreationOptions.None,
TaskContinuationOptions ContinuationOptions = TaskContinuationOptions.AttachedToParent,
CancellationToken CancellationToken = default,
: this(!ConfigureAwait ? ConfigureAwaitOptions.None : ConfigureAwaitOptions.ContinueOnCapturedContext,
CreationOptions, ContinuationOptions, CancellationToken, IsIOCentric) {}
public readonly TaskScheduler Scheduler=IsIOCentric ? TaskScheduler.Current : TaskScheduler.Default;
public static class ManagedProcessExtensions;
public class Processor([Optional] AsyncOptions options) : FullProcessor(options)
public override Processor processor => s_processor??=this;
public class ManagedProcessor([Optional] AsyncOptions options) : Processor(options)
public override ManagedProcessor processor => s_processor??=this;
public class FullProcessor([Optional] AsyncOptions options) : AsyncProcessor(options)
public override FullProcessor processor => s_processor??=this;
public interface IBaseProcessor
private protected const string baseThisNameWithMethodName = "Processor.Invoke";
protected internal virtual string TypeName => GetType().ToFriendlyName();
string createUnitName(string delegateTypeName, bool isAsync=false, string callerName="", string callerFilePath="", int callerLineNumber=0)
=> createUnitName(delegateTypeName, isAsync, new CallerInfo(callerName, callerFilePath, callerLineNumber));
string createUnitName(string delegateTypeName, bool isAsync=false, CallerInfo callerInfo=default)
=> string.Concat( baseThisNameWithMethodName, "(", delegateTypeName, ")",
isAsync ? "Async" : "", " <= ",
GetType().ToFriendlyName(), ".",
void logOutcome(UnitOfWorkLog log) {}
public class baseProcessor
private protected dynamic? s_processor;
public virtual baseProcessor processor => s_processor??=this;
private static ITimeable? s_timedProcess;
private protected static ITimeable s_timer => s_timedProcess??=new TimedProcess();
private protected const string baseThisNameWithMethodName = "Processor.Invoke";
private protected string? _typeName { get;set; }
protected internal virtual string TypeName => _typeName??=GetType().ToFriendlyName();
private protected string createUnitName(string delegateTypeName, bool isAsync=false, string callerName="", string callerFilePath="", int callerLineNumber=0)
=> createUnitName(delegateTypeName, isAsync, new CallerInfo(callerName, callerFilePath, callerLineNumber));
private protected string createUnitName(string delegateTypeName, bool isAsync=false, CallerInfo callerInfo=default)
=> string.Concat( baseThisNameWithMethodName, "(", delegateTypeName, ")",
isAsync ? "Async" : "", " <= ",
private protected void logOutcome(UnitOfWorkLog log) => WL(log.ToString());
public class SyncProcessor : SyncProcessorImpl, ISyncProcessor
public override SyncProcessor processor => s_processor??=this;
public void Invoke([DN] Expression<Action> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(x.Compile(), callerName, callerFilePath, callerLineNumber);
public void Invoke([DN] Action method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(method, callerName, callerFilePath, callerLineNumber);
public void Invoke<T>([DN] T input, [DN] Expression<Action<T>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(input, x.Compile(), callerName, callerFilePath, callerLineNumber);
public void Invoke<T>([DN] ref readonly T input, [DN] Expression<Action<T>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(input, x.Compile(), callerName, callerFilePath, callerLineNumber);
public void Invoke<T>([DN] ref readonly T input, [DN] Action<T> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(input, method, callerName, callerFilePath, callerLineNumber);
public void Invoke<T>([DN] T input, [DN] Action<T> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke<T>(input, method, callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<TResult>([DN] Expression<Func<TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(x.Compile(), callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<TResult>([DN] Func<TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke<TResult>(method, callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<T, TResult>([DN] T input, [DN] Expression<Func<T, TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(input, x.Compile(), callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, TResult>> x, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> Invoke(in input, x.Compile(), callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<T, TResult>([DN] ref readonly T input, [DN] Func<T, TResult> producer, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke(input, producer, callerName, callerFilePath, callerLineNumber);
public TResult? Invoke<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invoke<T, TResult>(input, method, callerName, callerFilePath, callerLineNumber);
public class AsyncProcessor([Optional] AsyncOptions options) : AsyncProcessorImpl(options), IAsyncTaskProcessor
public override AsyncProcessor processor => s_processor??=this;
public Task InvokeAsync([DN] Expression<Action> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync([DN] Action method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(method, @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] T input, [DN] Expression<Action<T>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] ref readonly T input, [DN] Expression<Action<T>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> InvokeAsync(in input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] ref readonly T input, [DN] Action<T> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] T input, [DN] Action<T> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<T>(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync([DN] Expression<Func<Task>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> InvokeAsync(x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync([DN] Func<Task> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<TResult>([DN] Expression<Func<TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<TResult>([DN] Func<TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<TResult>(method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Expression<Func<T, TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, TResult>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> InvokeAsync(in input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Func<T, TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<T, TResult>(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] T input, [DN] Expression<Func<T, Task>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] ref readonly T input, [DN] Expression<Func<T, Task>> x, [Optional] AsyncOptions @options,[CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] ref readonly T input, [DN] Func<T, Task> method, [Optional] AsyncOptions @options,[CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task InvokeAsync<T>([DN] T input, [DN] Func<T, Task> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<T>(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult> InvokeAsync<TResult>([DN] Expression<Func<Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult> InvokeAsync<TResult>([DN] Func<Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<TResult>(method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Expression<Func<T, Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Expression<Func<T, Task<TResult>>> x, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, x.Compile(), @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] ref readonly T input, [DN] Func<T, Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync(input, method, @options, callerName, callerFilePath, callerLineNumber);
public Task<TResult?> InvokeAsync<T, TResult>([DN] T input, [DN] Func<T, Task<TResult>> method, [Optional] AsyncOptions @options, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
=> invokeAsync<T, TResult>(input, method, @options, callerName, callerFilePath, callerLineNumber);
public abstract class SyncProcessorImpl : baseProcessor
protected internal void invoke([DN] Action method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var unitName=createUnitName("Action", false, callerName, callerFilePath, callerLineNumber);
logOutcome(this.Success(unitName, s_timer.Finish()));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal void invoke<T>([DN] T input, [DN] Action<T> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var unitName=createUnitName("Action<T>", false, callerName, callerFilePath, callerLineNumber);
logOutcome(this.Success(unitName, duration:s_timer.Finish()));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(unitName, ex.SourceException.Message, ex.SourceException));
protected internal TResult? invoke<TResult>([DN] Func<TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult result=default(TResult);
var unitName=createUnitName("Func<TResult>", false, callerName, callerFilePath, callerLineNumber);
logOutcome(this.Success(unitName, duration:s_timer.Finish()));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(unitName, ex.SourceException.Message, ex.SourceException));
protected internal TResult? invoke<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult result=default(TResult);
var unitName=createUnitName("Func<T, TResult>", false, callerName, callerFilePath, callerLineNumber);
result=method.Invoke(input);
logOutcome(this.Success(unitName, duration:s_timer.Finish()));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(unitName, ex.SourceException.Message, ex.SourceException));
public abstract class AsyncProcessorImpl([Optional] AsyncOptions classDefaultOptions) : SyncProcessor
private protected static AsyncOptions? s_classDefaultOptions = default;
public virtual AsyncOptions classOptions =>s_classDefaultOptions ??= classDefaultOptions;
protected internal async Task invokeAsync([DN] Action method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Action", true, callerName, callerFilePath, callerLineNumber) );
var task=Task.Factory.StartNew( ()=> method.Invoke(), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler );
await task.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), message:task.Exception!.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, ex.SourceException));
protected internal async Task invokeAsync<T>([DN] T input, [DN] Action<T> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Action<T>", true, callerName, callerFilePath, callerLineNumber) );
var task=Task.Factory.StartNew( ()=> method.Invoke(input), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler);
await task.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task<TResult?> invokeAsync<TResult>([DN] Func<TResult> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? result=default(TResult);
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<TResult>", true, callerName, callerFilePath, callerLineNumber) );
var task=Task.Factory.StartNew( ()=> method.Invoke(), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler );
result=await task!.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task<TResult?> invokeAsync<T, TResult>([DN] T input, [DN] Func<T, TResult> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? result=default(TResult);
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<T, TResult>", true, callerName, callerFilePath, callerLineNumber) );
var task=Task.Factory.StartNew( ()=> method.Invoke(input), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler );
result=await task!.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task invokeAsync([DN] Func<Task> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<Task>", true, callerName, callerFilePath, callerLineNumber) );
Task task=await Task.Factory.StartNew( ()=> method.Invoke(), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler )
.ContinueWith(antecedent => antecedent.Result, local.options.ContinuationOptions).ConfigureAwait(local.options.ConfigureAwaitOptions);
await task!.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task invokeAsync<T>([DN] T input, [DN] Func<T, Task> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<T, Task>", true, callerName, callerFilePath, callerLineNumber) );
Task task=await Task.Factory.StartNew( ()=> method.Invoke(input), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler )
.ContinueWith(antecedent => antecedent.Result, local.options.ContinuationOptions).ConfigureAwait(local.options.ConfigureAwaitOptions);
await task.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task<TResult?> invokeAsync<TResult>([DN] Func<Task<TResult>> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? result=default(TResult);
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<Task<TResult>>", true, callerName, callerFilePath, callerLineNumber) );
Task<TResult> task=await Task.Factory.StartNew<Task<TResult>>( ()=>method.Invoke(), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler)
.ContinueWith(antecedent => antecedent.Result, local.options.ContinuationOptions).ConfigureAwait(local.options.ConfigureAwaitOptions);
result=await task!.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
protected internal async Task<TResult?> invokeAsync<T, TResult>([DN] T input, [DN] Func<T, Task<TResult>> method, [Optional] AsyncOptions callOptions, [CMN] string callerName="", [CFP] string callerFilePath="", [CLN] int callerLineNumber=0)
TResult? result=default(TResult);
var local=( options: callOptions.Equals(default) ? classOptions : callOptions,
unitName: createUnitName("Func<T, Task<TResult>>", true, callerName, callerFilePath, callerLineNumber) );
var task=await Task.Factory.StartNew<Task<TResult>>( ()=> method.Invoke(input), local.options.CancellationToken, local.options.CreationOptions, local.options.Scheduler )
.ContinueWith(antecedent => antecedent.Result, local.options.ContinuationOptions).ConfigureAwait(local.options.ConfigureAwaitOptions);
result=await task!.ConfigureAwait(local.options.ConfigureAwaitOptions);
logOutcome(this.Success(local.unitName, duration:s_timer.Finish(), task.Exception?.Message, exception:task.Exception));
var ex=ExceptionDispatchInfo.Capture(e);
logOutcome(this.Failure(local.unitName, ex.SourceException.Message, exception:ex.SourceException));
public static class ProcessExtensions
public static UnitOfWorkLog Success(this baseProcessor processor, string? name, TimeSpan duration, [O] string? message, [O] Exception? exception)
=> new (name, duration, message, exception, true);
public static UnitOfWorkLog Failure(this baseProcessor processor, string? name, [O] string? message, [O] Exception? exception)
=> new (name, TimeSpan.Zero, message, exception, false);
namespace Core.Process.Logs
public class UnitOfWorkBase(string? name)
public UnitOfWorkBase(string? name, TimeSpan duration) : this(name) => Duration=duration;
public string? Name = name;
public TimeSpan Duration;
public class UnitOfWorkLog(string? unitName, [O] TimeSpan duration, [O] string? message, [O] Exception? exception) : UnitOfWorkBase(unitName, duration)
internal UnitOfWorkLog() : this(unitName:null){}
internal UnitOfWorkLog(string? name, [O] TimeSpan duration, [O] string? message, [O] Exception? exception, bool isSuccess=true) : this(name, duration, message, exception)
public string? Message = message;
public Exception? Exception = exception;
private DateTime DateTimeCreated = DateTime.UtcNow;
public bool CompletedSuccessfully => _isSuccess && Exception is null;
public override string ToString()=>
string.Concat($"(UnitOfWork: ",
$"=> {Name}, Duration: {Duration}",
!CompletedSuccessfully ? $", Successful: {CompletedSuccessfully}" : "",
Message is not null ? $", Message: {Message}" : "",
Exception is not null ? $", Exception: {Exception.Message}" : "",
$", DateTime: {DateTimeCreated})");
public static implicit operator UnitOfWorkLog((string unitName, TimeSpan duration, string? error, Exception? exception) tuple)
=> new UnitOfWorkLog(tuple.unitName, tuple.duration, tuple.error, tuple.exception);
public static implicit operator UnitOfWorkLog((TimeSpan duration, string? error, Exception? exception) tuple)
=> new UnitOfWorkLog(default, tuple.duration, tuple.error, tuple.exception);
public readonly struct SecurityToken(IdentityType identity, [O] IEnumerable<string>? errors, [O, DPV(20)] double expirationInMinutes)
private readonly List<string> _errors = new(errors ?? Enumerable.Empty<string>());
private readonly DateTime _start = dt.UtcNow;
public readonly IdentityType Identity => identity;
public readonly DateTime SessionStart => _start;
public readonly DateTime SessionExpiration => _start + TimeSpan.FromMinutes(expirationInMinutes);
public IEnumerable<ProcessInfo> AllowedActions => [new ProcessInfo("Core.Ingress.Services.IngressService<Person>","Process")];
public IEnumerable<string> Errors => _errors is null ? Enumerable.Empty<string>() : new List<string>(_errors);
public void AddError(string message) => _errors.Add(message);
namespace Core.Services{}
namespace Core.Services.Security
public class SecurityAuthenticationService(SecurityIdentityRetrievalDelegate? securityIdentityRetrievalDelegate) : ISecurityAuthenticatableService
private protected SecurityIdentityRetrievalDelegate _securityIdentityRetrievalDelegate=securityIdentityRetrievalDelegate ?? new SecurityIdentityRetrievalDelegate( ()=>"Bugs");
public bool TryAuthenticate(IdentityType identity, [Optional] out ISecurityToken? token)
public bool IsAuthenticated=>Token is not null;
public ISecurityToken? Token {get;set;}=new SecurityToken(securityIdentityRetrievalDelegate?.Invoke()!);
public class SecurityAuthorizationService(SecurityIdentityRetrievalDelegate securityIdentityRetrievalDelegate)
: SecurityAuthenticationService(securityIdentityRetrievalDelegate), ISecurityAuthorizableService
public bool TryAuthorize(ProcessInfo processInfo)
=> Token is not null && !Token.IsExpired && Token is ISecurityToken token ? token.AllowedActions.Contains(processInfo) : false;
public bool TryAuthorize<T>(Action<T> securedAction)
namespace Core.Services.Ingress
public interface IIngressConsumerService<T> : ICommand<T>, IServiceType;
public interface IIngressProducerService : IQuery, IServiceType;
public interface IIngressProducerService<T> : IQuery<T>, IServiceType;
public interface IIngressProducerService<TEntityId, T> : IQuery<TEntityId, T>, IQueryAsync<TEntityId, T>, IServiceType;
public interface IIngressConsumerProducerService<T, TResult> : ICommandQuery<T, TResult>;
public abstract class IngressServiceBase<T>(IBusinessService<T> businessService, ISecurityAuthorizableService securityService)
where T : IDomainObject<T>
private protected ISecurityAuthorizableService _securityService=securityService;
private protected IBusinessService<T> _businessService=businessService;
private protected record CachedDelegates(Action<T>? Process, Func<IEntityId, T?>? Get, Func<Expression<Predicate<T>>, IEnumerable<T>>? GetEnum);
private static CachedDelegates? s_delegates;
private protected CachedDelegates delegates => s_delegates ??= new( _businessService.Process, _businessService.Get, _businessService.Get );
public abstract class IngressConsumerProducerServiceBase<T,TResult>(IBusinessService<T,TResult> businessService, ISecurityAuthorizableService securityService)
where T : IDomainObject<T>
private protected ISecurityAuthorizableService _securityService=securityService;
private protected IBusinessService<T,TResult> _businessService=businessService;
private protected record CachedDelegates(Action<T>? @process);
private static CachedDelegates? s_delegates;
private protected CachedDelegates delegates = s_delegates ??= new (businessService.Process);
public class IngressService<T>(IBusinessService<T> businessService, ISecurityAuthorizableService securityService)
: IngressServiceBase<T>(businessService, securityService), IIngressConsumerService<T>
where T : IDomainObject<T>
public IngressService(IBusinessService<T> businessService, ISecurityAuthorizableService securityService, string test)
: this(businessService, securityService)
Hi(_test + "added ********************************");
public virtual void Process(T input)
WL("SECURITY CHECKING HERE");
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().Name})");
Hi("TryAuth:" + _securityService.TryAuthenticate("", out ISecurityToken? t));
Hi("canaccess:" + t.CanAccess(new ProcessInfo(TypeName, SRM.GetCurrentMethod().Name)));
Hi(["Delegate Process assigned to...", delegates.Process.Method.DeclaringType.ToFriendlyName(), delegates.Process.Method.Name]);
if(((IDomainObject<T>)input).IsActionable) processor.Invoke(input, delegates.Process!); else WL("Not Actionable");
public virtual Task ProcessAsync(T input)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().Name})");
public class IngressService<TEntityId, T>(IBusinessService<T> businessService, ISecurityAuthorizableService securer)
: IngressServiceBase<T>(businessService, securer), IIngressProducerService<TEntityId, T>
where T : IDomainObject<T>
where TEntityId : IEntityId
public virtual T? Get(TEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.Invoke<IEntityId, T?>(id, delegates.Get!) ?? IDomainObject<T>.Empty;
public virtual Task<T?> GetAsync(TEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.InvokeAsync<IEntityId, T?>(id, delegates.Get!);
public virtual Task RunAsync(TEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.InvokeAsync<IEntityId, T?>(id, delegates.Get!);
public virtual IEnumerable<T> Get(Expression<Predicate<T>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
return processor.Invoke(predicate, delegates.GetEnum!) ?? IDomainAggregate<T>.Empty;
public class IngressConsumerProducerService<T, TResult, TEntityId>(IBusinessService<T, TResult> businessService, ISecurityAuthorizableService securer)
: IngressConsumerProducerServiceBase<T, TResult>(businessService, securer), IIngressConsumerProducerService<T, TResult>
where T : IDomainObject<T>
where TEntityId : IEntityId
public TResult Process(T input)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().ToFriendlyName()})");
if(((IDomainObject<T>)input).IsActionable) processor.Invoke(input, delegates.process!); else WL("Not Actionable");
return default(TResult)!;
public virtual Task ProcessAsync(T input)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().Name})");
namespace Core.Services.Business
public interface IBusinessService : IQuery, ICommand, IServiceType;
public interface IBusinessService<T> : IQuery<T>, ICommand<T>, IServiceType;
public interface IBusinessService<T, TResult> : IQuery<TResult>, ICommand<T>, IServiceType;
public abstract class BusinessServiceBase<T>(IServiceProvider<T> serviceProvider)
where T : IDomainObject<T>
private protected record CachedDelegates(Action<T>? @process, Func<IEntityId, T?>? @get, Func<Expression<Predicate<T>>, IEnumerable<T>>? @getEnum);
private static CachedDelegates? s_delegates;
private protected CachedDelegates delegates = s_delegates ??= new (serviceProvider.Process, serviceProvider.Get, serviceProvider.Get);
public class BusinessService<T>(IServiceProvider<T> serviceProvider) : BusinessServiceBase<T>(serviceProvider), IBusinessService<T>
where T : IDomainObject<T>
public virtual void Process(T input)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().ToFriendlyName()})");
processor.Invoke(input, delegates.process!);
public virtual Task ProcessAsync(T input)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().Name})");
public virtual T? Get(IEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.Invoke<IEntityId, T?>(id, delegates.get!) ?? IDomainObject<T>.Empty;
public virtual IEnumerable<T> Get(Expression<Predicate<T>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
return processor.Invoke(predicate, delegates.getEnum!) ?? IDomainAggregate<T>.Empty;
namespace Core.Services.Providers
public interface IServiceProvider : IQuery, ICommand, IServiceType;
public interface IServiceProvider<T> : IQuery<T>, ICommand<T>, IServiceType;
public interface IServiceProvider<T, TResult> : IQuery<TResult>, ICommand<T>, IServiceType;
public abstract partial class ServiceProvider<T>([DN] IServiceProvider<T> serviceProvider)
: ManagedProcessor, IServiceProvider<T>
where T : IDomainObject<T>
private protected record CachedDelegates(Action<T>? @process, Func<IEntityId, T?>? @get, Func<Expression<Predicate<T>>, IEnumerable<T>>? @getEnum);
private static CachedDelegates? s_delegates;
private protected CachedDelegates delegates = s_delegates ??= new (serviceProvider.Process, serviceProvider.Get, serviceProvider.Get);
public virtual void Process(T input)
WL($"ServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().ToFriendlyName()})");
processor.Invoke(input, delegates.process!);
((IStateManager)input).Reset();
public T? Get(IEntityId id)
WL($"ServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
var result=processor.Invoke(id, delegates.get!);
return result ?? IDomainObject<T>.Empty;
public IEnumerable<T> Get(Expression<Predicate<T>> predicate)
WL($"ServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
var result=processor.Invoke(predicate, delegates.getEnum!);
result?.For(r=>PostProcess(r));
return result ?? IDomainAggregate<T>.Empty;
partial void PreProcess(T input);
partial void PostProcess( T output);
public abstract partial class DataServiceProvider<T>([DN] ICrudProvider<T> serviceProvider)
: ManagedProcessor, IServiceProvider<T>
where T : IDomainObject<T>
private protected ICrudProvider<T> _serviceProvider=serviceProvider;
private protected record CachedDelegates(Func<T, T?>? @add, Action<T>? @update, Action<T>? @delete, Func<IdType, T>? @read, Func<Expression<Predicate<T>>, IDomainAggregate<T>>? @readEnum);
private static CachedDelegates? s_delegates;
private protected CachedDelegates delegates = s_delegates ??= new (serviceProvider.Add, serviceProvider.Update, serviceProvider.Delete, serviceProvider.Read, serviceProvider.Read);
public virtual void Process( T input)
WL($"DataServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().ToFriendlyName()})");
if(!input.State.IsActionable) return;
if(!((IActionable)input).IsActionable) return;
switch( input.State.ToCrudOperation() )
case CrudOperation.Insert : processor.Invoke(input, Add); break;
case CrudOperation.Delete : processor.Invoke(input, Delete); break;
case CrudOperation.Update : Hi();processor.Invoke(input, _serviceProvider.Update); break;
WriteLine("No state, so do nothing to...\n\t" + input.ToString());break;
public virtual void Process(T input, Expression<Action<T>> action)
WL($"DataServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({input.GetType().ToFriendlyName()})");
processor.Invoke<T>(input, action);
public T? Get([DN] IEntityId id)
WL($"DataServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.Invoke(id, Read) ?? IDomainObject<T>.Empty;
public IEnumerable<T> Get(Expression<Predicate<T>> predicate)
WL($"DataServiceProvider<T> <-- {TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
return processor.Invoke(predicate, Read) ?? IDomainAggregate<T>.Empty;
partial void PreProcess(ref T input);
partial void PostProcess(ref T output);
protected abstract T? Add(T input);
protected abstract void Update(T input);
protected abstract void Delete(T input);
protected abstract T? Read(IEntityId id);
protected abstract IEnumerable<T> Read(Expression<Predicate<T>> predicate);
public class RdbmsProvider<T>(ICrudProvider<T> serviceProvider)
: DataServiceProvider<T>(serviceProvider)
where T : IDomainObject<T>
protected override T? Add(T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
WL("Valid to insert: " + ((IActionable)obj).IsCreatable);
if(!((IActionable)obj).IsCreatable)
return processor.Invoke(obj, delegates.add!);
protected override void Update(T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
processor.Invoke(obj, delegates.update!);
protected override void Delete( T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
processor.Invoke(obj, delegates.delete!);
protected override T? Read([DN] IEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.Invoke(id!.Id, delegates.read!) ?? IDomainObject<T>.Empty;
protected override IEnumerable<T> Read(Expression<Predicate<T>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
return processor.Invoke(predicate, delegates.readEnum!) ?? IDomainAggregate<T>.Empty;
public class SqlProvider<T>(ICrudProvider<T> serviceProvider)
: DataServiceProvider<T>(serviceProvider)
where T : IDomainObject<T>
protected override T Add(T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
if(!((IActionable)obj).IsCreatable)
WL("INVALID TO INSERT\n");
return processor.Invoke(obj, delegates.add!);
protected override void Update(T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
processor.Invoke(obj, delegates.update!);
protected override void Delete(T obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
processor.Invoke(obj, delegates.delete!);
protected override T? Read(IEntityId id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return processor.Invoke(id.Id, delegates.read!) ?? IDomainObject<T>.Empty;
protected override IEnumerable<T> Read(Expression<Predicate<T>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
return processor.Invoke(predicate, delegates.readEnum!) ?? IDomainAggregate<T>.Empty;
public class EmailProvider<T>() : ServiceProvider<T>(null)
where T : IDomainObject<T>
public void Test(string s, string x)
private void Test<TT,S>(DateTime d, TT t, S tt, double p)
public void Test(DateTime d, string t, int tt, double p)
public class SqlPersonProvider
: ManagedProcessor, ICrudProvider<Person>
public Person Add(Person obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
((IEntityObjectId<PersonId>)obj).SetId(new PersonId(IEntityId.NewId));
public void Update(Person obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
Data.People.RemoveAll(p=>p.Id==obj.Id);
public void Delete(Person obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
public Person Read(IdType id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return Data.People.Single(p=>p.Id==id) ?? IDomainObject<Person>.Empty!;
public IDomainAggregate<Person> Read(Expression<Predicate<Person>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
var pp=Data.People.Where(p=>predicate.Compile()(p)).ToArray();
public class SqlEmployeeProvider
: ManagedProcessor, ICrudProvider<Employee>
public Employee Add(Employee obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
((IEntityObjectId<EmployeeId>)obj).SetId(new EmployeeId(IEntityId.NewId));
public void Update(Employee obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
Data.Employees.RemoveAll(e=>e.Id==obj.Id);
public void Delete(Employee obj)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({obj.GetType().ToFriendlyName()})");
Data.Employees.Remove(obj);
public Employee Read(IdType id)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({id.GetType().ToFriendlyName()})");
return Data.Employees.Single(i=>i.Id==id) ?? IDomainObject<Employee>.Empty;
public IDomainAggregate<Employee> Read(Expression<Predicate<Employee>> predicate)
WL($"{TypeName}.{SRM.GetCurrentMethod()?.Name}({predicate.GetType().ToFriendlyName()})");
var ee=Data.Employees.Where(e=>predicate.Compile()(e)).ToArray();
public delegate void DomainObjectEventHandler(object sender);
public delegate void DomainObjectEventHandler<T>(T sender) where T : IStateManaged;
public readonly record struct EntityId(IdType Id) : IEntityId
public static IEntityId<IdType> Empty => new EntityId<IdType>(default);
public static implicit operator IdType(EntityId uniqueId)=>uniqueId.Id;
public readonly record struct EntityId<T>(T Id) : IEntityId<T>
public static IEntityId<T> Empty => new EntityId<T>(default);
public static implicit operator T(EntityId<T> uniqueId)=>uniqueId.Id;
public readonly record struct PersonId(IdType Id) : IEntityId
public static PersonId Empty => new(default);
public static implicit operator IdType(PersonId uniqueId)=>uniqueId.Id;
public readonly record struct EmployeeId(IdType Id) : IEntityId
public static EmployeeId Empty => new(default);
public static implicit operator IdType(EmployeeId uniqueId)=>uniqueId.Id;
public readonly record struct AgeId(IdType Id) : IEntityId
public static AgeId Empty => new(default);
public static implicit operator IdType(AgeId uniqueId)=>uniqueId.Id;
public struct Value<T>(Action setDirtyAction)
private Action _setDirtyAction=setDirtyAction;
public Value(T value, Action setDirtyAction) : this(setDirtyAction) { _value=value;}
public void Set(in T value, [CMN] string caller="")
if(isReallyChanged(value)) { _value=value; _setDirtyAction?.Invoke(); }
private bool isReallyChanged(in T value)
=> (_value is null && value is not null) || (_value is not null && value is null) || (_value is not null && !_value.Equals(value));
public static implicit operator T? (Value<T> valueObject)=>valueObject._value;
public class ObjectState : IStateful
protected bool _isNew=true, _isDirty, _isDeleted;
internal ObjectState(bool isNew, bool isDirty, bool isDeleted)
=> ( _isNew, _isDirty, _isDeleted ) = ( isNew, isDirty, isDeleted );
bool IStateful.IsNew =>_isNew;
bool IStateful.IsDirty =>_isDirty;
bool IStateful.IsDeleted =>_isDeleted;
public class ObjectStateManager : ObjectState, IStateManager
public event DomainObjectEventHandler? ItemRemoved;
public event DomainObjectEventHandler? ItemChanged;
private IStateManager _stateManager;
public ObjectStateManager() =>_stateManager=(IStateManager)this;
public ObjectStateManager(DomainObjectEventHandler onItemRemoved) : this() => _stateManager.ItemRemoved+=onItemRemoved;
public virtual void SetDeleted()
( _isNew, _isDirty, _isDeleted ) = (false, false, true);
WL($"ObjectStateManager.{SRM.GetCurrentMethod()?.Name}: {this.ToString()}");
ItemRemoved?.Invoke(this);
public void SetDirty(bool isDirty=true)
_isDirty = _isDeleted || _isNew ? false : isDirty;
ItemChanged?.Invoke(this);
public void SetNew(bool isNew=true) => ( _isNew, _isDirty, _isDeleted ) = (isNew, false, false);
public void Reset() => _isNew=_isDirty=_isDeleted=false;
public class DomainObject : IDomainObject, IEntityId
public event DomainObjectEventHandler? ItemRemoved;
public event DomainObjectEventHandler? ItemChanged;
protected IStateManager stateManager=new ObjectStateManager();
stateManager.ItemRemoved+=OnItemRemoved;
stateManager.ItemChanged+=OnItemChanged;
protected DomainObject(IdType id) : this() => SetId(id);
protected DomainObject(DomainObjectEventHandler onItemRemoved) : this()
=> stateManager.ItemRemoved+=onItemRemoved;
protected DomainObject(DomainObjectEventHandler onItemRemoved, DomainObjectEventHandler onItemChanged) : this(onItemRemoved)
=> stateManager.ItemChanged+=onItemChanged;
public IdentityType? Identity { get;set; }
public void SetDeleted() => stateManager.SetDeleted();
protected void SetDirty() => stateManager.SetDirty();
protected bool IsActionable => stateManager.IsActionable;
protected bool IsCreatable => _id==0 && stateManager.IsNew;
IStateManager IStateManaged.State => stateManager;
protected void SetId([DN] IdType id)
if(id.Equals(IEntityId.Empty)) return;
stateManager.SetNew(false);
protected virtual void OnItemRemoved([DN] object sender)
WL($"DomainObject.{SRM.GetCurrentMethod()?.Name}: {sender.ToString()}");
ItemRemoved?.Invoke(this);
protected virtual void OnItemChanged([DN] object sender)
WL($"DomainObject.{SRM.GetCurrentMethod()?.Name}: {sender.ToString()}");
ItemChanged?.Invoke(this);
public override string ToString() => $"Id: {Id.ToString()}\n{base.ToString()}";
internal static class DomainAggregateBuilder { internal static DomainAggregate<IDomainObject> Create(ReadOnlySpan<IDomainObject> items) => new DomainAggregate<IDomainObject>(items); }
[CollectionBuilder(typeof(DomainAggregateBuilder),"Create")]
public partial class DomainAggregate<T> : IDomainAggregate<T>, IActionable, IEmptyAggregateObject<T>
private protected IDomainAggregate<T> iDomainAggregate;
private DomainAggregate() => iDomainAggregate=(IDomainAggregate<T>)this;
public DomainAggregate(ReadOnlySpan<T> items, [Optional] Func<T> transformer) : this() => ((IDomainAggregate<T>)this).AddItems(items);
public IList<T> Items {get;}=new List<T>();
public class Person : DomainObject, IEntityObjectId<PersonId>, IDomainObject<Person>, IActionable, IEmptyObject<Person>
private Value<string> _first, _last;
private PersonId _entityId=PersonId.Empty;
public Person() => (_first, _last) = ( new Value<string>(SetDirty), new Value<string>(SetDirty) );
public Person(IdType id, string first, string last) : base(id)
=> ( _first, _last ) = ( new Value<string>(first, SetDirty), new Value<string>(last, SetDirty) );
public PersonId EntityId => _entityId==PersonId.Empty ? _entityId=new PersonId(base.Id) : _entityId;
public string First { get=>_first!; set=>_first.Set(value); }
public string Last { get=>_last!; set=>_last.Set(value); }
bool IActionable.IsCreatable => base.IsCreatable && ( !String.IsNullOrWhiteSpace(First) && !String.IsNullOrWhiteSpace(Last) );
void IEntityObjectId<PersonId>.SetId(PersonId id)
_entityId=_entityId with {Id=id};
public override string ToString()=>base.ToString() + $", First: {First}, Last: {Last}";
public class Employee : Person, IEntityObjectId<EmployeeId>, IDomainObject<Employee>, IActionable, IEmptyObject<Employee>
private Value<string> _title;
private EmployeeId _entityId= EmployeeId.Empty;
public Employee() => _title=new Value<string>(SetDirty);
public Employee(IdType id, string title, string first, string last) : base(id, first, last)
=> _title=new Value<string>(title, SetDirty);
public new EmployeeId EntityId =>_entityId==EmployeeId.Empty ? _entityId=new EmployeeId(base.Id) : _entityId;
public string Title { get=>_title!; set=>_title.Set(value); }
bool IActionable.IsCreatable=>base.IsCreatable && ( !String.IsNullOrWhiteSpace(Title) );
void IEntityObjectId<EmployeeId>.SetId(EmployeeId id) { base.SetId(id.Id); _entityId=_entityId with {Id=id}; }
public override string ToString()=> base.ToString() + $", Title: {Title} ";
public struct Age : IDomainObject, IActionable, IEntityObjectId<AgeId>, IEntityId
public event DomainObjectEventHandler? ItemChanged;
public event DomainObjectEventHandler? ItemRemoved;
private AgeId _entityId=AgeId.Empty;
private Value<int> _value;
private IStateManager stateManager=new ObjectStateManager();
public Age() => _value=new Value<int>(SetDirty);
public Age(IdType id, int value)
((IEntityObjectId<AgeId>)this).SetId(new AgeId(id));
_value=new Value<int>(value, SetDirty);
public AgeId EntityId =>_entityId==AgeId.Empty ? _entityId=new AgeId(Id) : _entityId;
IStateManager IStateManaged.State => stateManager;
public IdentityType? Identity { get;set; }
public int Value {get=>_value;set=>_value.Set(value);}
public void SetDirty() => stateManager.SetDirty(true);
bool IActionable.IsActionable => true;
bool IActionable.IsCreatable => Value!=0;
void IEntityObjectId<AgeId>.SetId(AgeId id)
if(id.Equals(IEntityId.Empty)) return;
_entityId=_entityId with {Id=id};
stateManager.SetNew(false);
internal static class PeopleAggregateBuilder { internal static People Create(ReadOnlySpan<Person> items) => new People(items); }
[CollectionBuilder(typeof(PeopleAggregateBuilder),"Create")]
public class People : DomainAggregate<Person>
public People(params ReadOnlySpan<Person> people) : base (people){}
internal static class EmployeesAggregateBuilder { internal static Employees Create(ReadOnlySpan<Employee> items) => new Employees(items); }
[CollectionBuilder(typeof(EmployeesAggregateBuilder),"Create")]
public class Employees : DomainAggregate<Employee>
public Employees(params ReadOnlySpan<Employee> employees) : base (employees){}
public enum Accessibility : byte
public enum CrudOperation : byte
public enum ObjectLifetime : byte
namespace Core.Dependencies
using DAM=System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute;
using DAMT=System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes;
using SCI=System.Collections.Immutable;
using NN=System.Diagnostics.CodeAnalysis.NotNullAttribute;
using NNW=System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;
using O=System.Runtime.InteropServices.OptionalAttribute;
using DPV=System.Runtime.InteropServices.DefaultParameterValueAttribute;
using Core.Dependencies.Delegates;
using Core.Dependencies.Interfaces;
public delegate Type TypeResolverDelegate(Type serviceType);
public delegate string TypeNameResolverDelegate(Type serviceType);
public delegate object DependencyTypeFactoryDelegate(Type serviceType);
public interface ILazyDependency<T>
public bool IsValueCreated {get;}
public interface ILazyDependency<T, TMetadata> : ILazyDependency<T>
public TMetadata Metadata {get;}
public enum InversionType
public class LazyDependency<T> : Lazy<T>, ILazyDependency<T>
public LazyDependency(Func<T> function) : base(function){}
public LazyDependency(T value) : base(value){}
public LazyDependency(bool isThreadSafe) : base(isThreadSafe){}
public LazyDependency(LazyThreadSafetyMode mode) : base(mode){}
public LazyDependency(Func<T> function, bool isThreadSafe) : base(function, isThreadSafe){}
public LazyDependency(Func<T> function, LazyThreadSafetyMode mode) : base(function, mode){}
public static implicit operator T (LazyDependency<T> lazyDependency) => lazyDependency.Value;
public class LazyDependency<T, TMetadata> : Lazy<T, TMetadata>, ILazyDependency<T, TMetadata>
public LazyDependency(TMetadata metadata) : base(metadata){}
public LazyDependency(Func<T> function, TMetadata metadata) : base(function, metadata){}
public LazyDependency(TMetadata metadata, bool isThreadSafe) : base(metadata, isThreadSafe){}
public LazyDependency(TMetadata metadata, LazyThreadSafetyMode mode) : base(metadata, mode){}
public LazyDependency(Func<T> function, TMetadata metadata, bool isThreadSafe) : base(function, metadata, isThreadSafe){}
public LazyDependency(Func<T> function, TMetadata metadata, LazyThreadSafetyMode mode) : base(function, metadata, mode){}
public static implicit operator T (LazyDependency<T, TMetadata> lazyDependency) => lazyDependency.Value;
public record class DependencyOptions(ObjectLifetime Lifetime=ObjectLifetime.Transient, bool EnforceAssignability=true, bool EnforceAbstractionToImplementationRelationship=false, object? Alias=null, Accessibility FieldAccessibilityInjected=Accessibility.AllButPublic, Func<dynamic>? Factory=null, bool AllowOverwrite=false)
public void Deconstruct(out ObjectLifetime lifetime, out bool enforceAssignability, out bool enforceAbstractionToImplementationRelationship, out object? alias, out Accessibility fieldAccessibilityInjected, out Func<dynamic>? factory, out bool allowOverwrite)
=> (lifetime, enforceAssignability, enforceAbstractionToImplementationRelationship, alias, fieldAccessibilityInjected, factory, allowOverwrite)=
(Lifetime, EnforceAssignability, EnforceAbstractionToImplementationRelationship, Alias, FieldAccessibilityInjected, Factory, AllowOverwrite);
internal static class DependencyDictionaryBuilder
internal static DependencyDictionary Create(ReadOnlySpan<Dependency> dependencies) => new DependencyDictionary(dependencies);
internal static DependencyDictionary Create( (Type serviceType, Type implementationType) [] dependencies)
=> new DependencyDictionary(new ReadOnlySpan<Dependency>(dependencies.Select(d=>new Dependency(d.serviceType, d.implementationType)).ToArray()));
[CollectionBuilder(typeof(DependencyDictionaryBuilder),"Create")]
public class DependencyDictionary : IEnumerable, IEnumerable<KeyValuePair<Type, ValidatedDependency>>
private Lazy<ConcurrentDictionary<Type, ValidatedDependency>> l_registrations=new Lazy<ConcurrentDictionary<Type, ValidatedDependency>>( ()=>new() );
private ConcurrentDictionary<Type, ValidatedDependency> _registrations=>l_registrations.Value;
public DependencyDictionary(params ReadOnlySpan<Dependency> dependencies) => AddDependencies(dependencies);
public void AddDependency([DN] Dependency dependency)
validateDependencyBeforeAdd(dependency, out ValidatedDependency? validatedDependency);
if(validatedDependency is not null) _registrations.AddOrUpdate(validatedDependency.ServiceType, key=>validatedDependency, (key, old)=>validatedDependency);
public void AddDependencies([DN] ReadOnlySpan<Dependency> dependencies)
{ for(var c=0;c<dependencies.Length;c++) AddDependency(dependencies[c]); }
public bool ContainsDependency(Type serviceType) => _registrations.ContainsKey(serviceType);
public ValidatedDependency? GetDependency<TServiceType>() => this[typeof(TServiceType)];
public ValidatedDependency? GetDependency(Type serviceType) => this[serviceType];
public bool TryAddDependency([DN] Dependency dependency)
=> Try(dependency, AddDependency);
public bool TryAddDependencies([DN] ReadOnlySpan<Dependency> dependencies)
=> Try(dependencies, AddDependencies);
public bool TryGetValidatedDependency<TServiceType>(out ValidatedDependency? validatedDependency)
=> TryGetValidatedDependency(typeof(TServiceType), out validatedDependency);
public bool TryGetValidatedDependency(Type serviceType, out ValidatedDependency? validatedDependency)
=> (validatedDependency = this[serviceType]) is not null;
private void validateDependencyBeforeAdd([DN] Dependency dependency, out ValidatedDependency? validatedDependency)
(List<Exception> exceptions, AggregateException? aggregateException, validatedDependency)=(new(), null, null);
if(TryGetValidatedDependency(dependency.ServiceType, out ValidatedDependency? existingDependency) && existingDependency is not null && !existingDependency.Options.AllowOverwrite)
exceptions.Add(Exceptions.DuplicateRegistration);
{ validatedDependency=validateDependency(dependency); }
catch (AggregateException e)
if(aggregateException is not null)
exceptions.AddRange(aggregateException.InnerExceptions);
throw Exceptions.CreateDependencyInversionAggregate(exceptions);
private protected ValidatedDependency validateDependency([DN] Dependency dependency)
var exceptions=new List<Exception>();
if(dependency.Options.EnforceAssignability && !dependency.IsAssignable)
exceptions.Add(Exceptions.AssignabilityViolation);
if(dependency.Options.EnforceAbstractionToImplementationRelationship && !dependency.ServiceType.IsInterface && !dependency.ServiceType.IsAbstract)
exceptions.Add(Exceptions.AbstractionToImplementationViolation);
if(dependency.Options.EnforceAbstractionToImplementationRelationship && dependency.ServiceType.FullName==dependency.ImplementationType.FullName)
exceptions.Add(Exceptions.IdenticalTypes);
if(!dependency.ImplementationType.IsClass)
exceptions.Add(Exceptions.IsNotClass);
if(dependency.ImplementationType.IsValueType)
exceptions.Add(Exceptions.IsValueType);
if(!Enumerable.TryGetNonEnumeratedCount(exceptions, out int count)) count=exceptions.Count();
if(count>0) throw Exceptions.CreateAggregate(exceptions);
return new ValidatedDependency(dependency);
public int Length => getLength(_registrations);
private int getLength<T>(IEnumerable<T> e) => !Enumerable.TryGetNonEnumeratedCount(e, out int length) ? e.Count() : length;
public IEnumerable<KeyValuePair<Type, ValidatedDependency>> Items => _registrations;
public IEnumerable<ValidatedDependency> Values => _registrations.Values;
public ValidatedDependency? this [Type type]
=> ContainsDependency(type) ? _registrations[type] : null;
public IEnumerator<KeyValuePair<Type, ValidatedDependency>> GetEnumerator()
=> _registrations!.GetEnumerator();
IEnumerator<KeyValuePair<Type, ValidatedDependency>> IEnumerable<KeyValuePair<Type, ValidatedDependency>>.GetEnumerator()
=> _registrations!.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> _registrations!.GetEnumerator();
public class InversionOfControl() : DependencyInversion(false)
public InversionOfControl([DN] params ReadOnlySpan<Dependency> dependencies) : this() => addServices(dependencies);
public TServiceType New<TServiceType>()
public TServiceType New<TServiceType>(params ReadOnlySpan<object> parameterValues)
=> @get<TServiceType>(parameterValues);
public TServiceType New<TServiceType, TImplementationType>()
=> @new<TServiceType, TImplementationType>();
public TServiceType New<TServiceType, TImplementationType>(params ReadOnlySpan<object> parameterValues)
=> @new<TServiceType, TImplementationType>(parameterValues:parameterValues);
public TServiceType New<TServiceType, TImplementationType>(DependencyOptions options, params ReadOnlySpan<object> parameterValues)
=> @new<TServiceType, TImplementationType>(options, parameterValues);
public TServiceType New<TServiceType, TImplementationType>([O, DPV(ObjectLifetime.Transient)] ObjectLifetime lifetime, params ReadOnlySpan<object> parameterValues)
=> @new<TServiceType, TImplementationType>(defaultOptions with {Lifetime=lifetime}, parameterValues);
public TServiceType? New<TServiceType>([DN] Func<dynamic> factory, [O, DPV(ObjectLifetime.Transient)] ObjectLifetime lifetime)
=> @new<TServiceType>(factory, lifetime);
private TServiceType @get<TServiceType>(params ReadOnlySpan<object> parameterValues)
=> (TServiceType)getService(typeof(TServiceType), parameterValues:parameterValues)!;
private TServiceType @new<TServiceType>([DN] Func<dynamic> factory, [O, DPV(ObjectLifetime.Transient)] ObjectLifetime lifetime)
addService(typeof(TServiceType), defaultOptions with {Factory=factory, Lifetime=lifetime, AllowOverwrite=true});
return @get<TServiceType>();
private TServiceType @new<TServiceType, TImplementationType>([O] DependencyOptions? options, params ReadOnlySpan<object> parameterValues)
var (serviceType, implementationType) = (typeof(TServiceType), typeof(TImplementationType));
if(implementationType is null) addService(serviceType, (options ?? defaultOptions) with {AllowOverwrite=true});
else addService(new Dependency(serviceType, implementationType, (options ?? defaultOptions) with {AllowOverwrite=true}));
return @get<TServiceType>(parameterValues);
private object? @new(Dependency dependency, params ReadOnlySpan<object> parameterValues)
base.addService(dependency);
return getService(dependency.ServiceType, parameterValues);
public class DependencyInjection() : DependencyInversion(true), System.IServiceProvider
private object _password;
static DependencyInjection() {}
public DependencyInjection([DN] params ReadOnlySpan<Dependency> dependencies) : this() => addServices(dependencies);
public DependencyInjection([DN] DependencyOptions defaultDependencyOptions) : this() => defaultOptions=defaultDependencyOptions;
public DependencyInjection([DN] TypeResolverDelegate typeResolver) : this() => TypeResolver=typeResolver;
public DependencyInjection([DN] TypeNameResolverDelegate typeNameResolver) : this() => TypeNameResolver=typeNameResolver;
public DependencyInjection([DN] TypeResolverDelegate typeResolver, [DN] TypeNameResolverDelegate typeNameResolver) : this()
=> (TypeResolver, TypeNameResolver)=(typeResolver, typeNameResolver);
public void AddTransient([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType)
=> AddService(serviceType, implementationType, lifetime:ObjectLifetime.Transient);
public void AddTransient<TServiceType, TImplementationType>() => AddTransient(typeof(TServiceType), typeof(TImplementationType));
public void AddTransient<TServiceType>() => AddService(typeof(TServiceType), defaultOptions);
public void AddThreadScoped([DN] Type serviceType, [DAM(DAMT.PublicConstructors | DAMT.Interfaces), DN] Type implementationType)
=> AddService(serviceType, implementationType, lifetime:ObjectLifetime.ThreadScoped);
public void AddThreadScoped<TServiceType, TImplementationType>() => AddThreadScoped(typeof(TServiceType), typeof(TImplementationType));
public void AddThreadScoped<TServiceType>() => AddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.ThreadScoped});
public void AddFlowScoped([DN] Type serviceType, [DAM(DAMT.PublicConstructors | DAMT.Interfaces), DN] Type implementationType)
=> AddService(serviceType, implementationType, lifetime:ObjectLifetime.FlowScoped);
public void AddFlowScoped<TServiceType, TImplementationType>() => AddFlowScoped(typeof(TServiceType), typeof(TImplementationType));
public void AddFlowScoped<TServiceType>() => AddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.FlowScoped});
public void AddSingleton([DN] Type serviceType, [DAM(DAMT.PublicConstructors | DAMT.Interfaces), DN] Type implementationType)
=> AddService(serviceType, implementationType, lifetime:ObjectLifetime.Singleton);
public void AddSingleton<TServiceType, TImplementationType>() => AddSingleton(typeof(TServiceType), typeof(TImplementationType));
public void AddSingleton<TServiceType>() => AddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.Singleton});
public void AddService<TServiceType, TImplementationType>() => AddService( dependency:new Dependency( serviceType:typeof(TServiceType), implementationType:typeof(TImplementationType) ) );
public void AddService<TServiceType, TImplementationType>(ObjectLifetime lifetime) => AddService( dependency:new Dependency( serviceType:typeof(TServiceType), implementationType:typeof(TImplementationType), defaultOptions with {Lifetime=lifetime} ));
public void AddService<TServiceType, TImplementationType>(DependencyOptions options)=> addService(new Dependency(typeof(TServiceType), typeof(TImplementationType), options));
public void AddService([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType, ObjectLifetime lifetime=ObjectLifetime.Transient)
=> AddService( dependency:new Dependency( serviceType:serviceType, implementationType:implementationType, defaultOptions with {Lifetime=lifetime} ) );
public void AddService([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType, DependencyOptions options)
=> AddService(dependency:new Dependency(serviceType, implementationType, options));
public void AddService([DN] Type serviceType) => AddService(serviceType, defaultOptions);
public void AddService([DN] Type serviceType, DependencyOptions options) => addService(serviceType, options);
public void AddService<TServiceType>([DN] Func<TServiceType> factory) where TServiceType : class
=> AddService<TServiceType>(factory, defaultOptions);
public void AddService<TServiceType>([DN] Func<TServiceType> factory, DependencyOptions options) where TServiceType : class
=> AddService<TServiceType, dynamic>( options with {Factory=factory} );
public void AddService([DN] string serviceTypeFullName, [DAM(DAMT.PublicConstructors), DN] string implementationTypeFullName, DependencyOptions options)
=> AddService(dependency:new Dependency(Type.GetType(serviceTypeFullName, true, true)!, Type.GetType(implementationTypeFullName, true, true)!, options));
public void AddService([DN]Dependency dependency) => addService(dependency);
public void AddServices([DN] params ReadOnlySpan<(Type serviceType, Type implementationType)> dependencies) => AddServices(dependencies);
public void AddServices([DN] params ReadOnlySpan<Dependency> dependencies) => addServices(dependencies);
public bool TryAddTransient([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType)
=> TryAddService(serviceType, implementationType, lifetime:ObjectLifetime.Transient);
public bool TryAddTransient<TServiceType, TImplementationType>() => TryAddTransient(typeof(TServiceType), typeof(TImplementationType));
public bool TryAddTransient<T>() => TryAddService(typeof(T), defaultOptions);
public bool TryAddThreadScoped([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType)
=> TryAddService(serviceType, implementationType, lifetime:ObjectLifetime.ThreadScoped);
public bool TryAddThreadScoped<TServiceType, TImplementationType>() => TryAddThreadScoped(typeof(TServiceType), typeof(TImplementationType));
public bool TryAddThreadScoped<TServiceType>() => TryAddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.ThreadScoped});
public bool TryAddFlowScoped([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType)
=> TryAddService(serviceType, implementationType, lifetime:ObjectLifetime.FlowScoped);
public bool TryAddFlowScoped<TServiceType, TImplementationType>() => TryAddFlowScoped(typeof(TServiceType), typeof(TImplementationType));
public bool TryAddFlowScoped<TServiceType>() => TryAddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.FlowScoped});
public bool TryAddSingleton([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType)
=> TryAddService(serviceType, implementationType, lifetime:ObjectLifetime.Singleton);
public bool TryAddSingleton<TServiceType, TImplementationType>() => TryAddSingleton(typeof(TServiceType), typeof(TImplementationType));
public bool TryAddSingleton<TServiceType>() => TryAddService(typeof(TServiceType), defaultOptions with {Lifetime=ObjectLifetime.Singleton});
public bool TryAddService([DN] Type serviceType, [O, DPV(null)] DependencyOptions? options) => tryAddService(serviceType, options);
public bool TryAddService<TServiceType>([DN] Func<TServiceType> factory) where TServiceType : class
=> TryAddService<TServiceType>(factory, options:defaultOptions);
public bool TryAddService<TServiceType>([DN] Func<TServiceType> factory, [DN] DependencyOptions options) where TServiceType : class
=> TryAddService<TServiceType, dynamic>( options:options with {Factory=factory} );
private bool TryAddService<TServiceType>() => TryAddService(serviceType:typeof(TServiceType), defaultOptions);
public bool TryAddService<TServiceType, TImplementationType>() => TryAddService( dependency:new Dependency( serviceType:typeof(TServiceType), implementationType:typeof(TImplementationType) ) );
public bool TryAddService<TServiceType, TImplementationType>([DN] ObjectLifetime lifetime) => TryAddService( dependency:new Dependency( serviceType:typeof(TServiceType), implementationType:typeof(TImplementationType), defaultOptions with {Lifetime=lifetime} ));
public bool TryAddService<TServiceType, TImplementationType>([DN] in DependencyOptions options)=> tryAddService(new Dependency(typeof(TServiceType), typeof(TImplementationType), options));
public bool TryAddService([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType, ObjectLifetime lifetime=ObjectLifetime.Transient)
=> TryAddService( dependency:new Dependency( serviceType:serviceType, implementationType:implementationType, defaultOptions with {Lifetime=lifetime} ) );
public bool TryAddService([DN] Type serviceType, [DAM(DAMT.PublicConstructors), DN] Type implementationType, DependencyOptions options)
=> TryAddService(dependency:new Dependency(serviceType, implementationType, options));
public bool TryAddService([DN] Dependency dependency) => tryAddService(dependency);
public bool TryAddService([DN] string serviceTypeFullName, [DN] string implementationTypeFullName, DependencyOptions options)
=> TryAddService(dependency:new Dependency(Type.GetType(serviceTypeFullName, true, true)!, Type.GetType(implementationTypeFullName, true, true)!, options));
public bool TryAddServices([DN] ReadOnlySpan<(Type serviceType, Type implementationType)> dependencies)
for(var c=0;c<dependencies.Length;c++)
if(!TryAddService(dependencies[c].serviceType, dependencies[c].implementationType)) return false;
public bool TryAddServices([DN] params ReadOnlySpan<Dependency> dependencies) => tryAddServices(dependencies);
public MethodInfo? GetMethodInfo<TServiceType>([DN] string methodName)
=> GetService<TServiceType>().GetType().GetMethods().Single(m=>methodName==m.Name);
public System.Delegate GetDelegate<TServiceType,TDelegateType>(string methodName)
where TDelegateType : System.Delegate
=> typeof(TServiceType).GetMethod(methodName).CreateDelegate<TDelegateType>();
public TServiceType GetService<TServiceType>()
=> (TServiceType?)getService(typeof(TServiceType));
public TServiceType GetService<TServiceType>(params ReadOnlySpan<object> parameterValues)
=> (TServiceType?)getService(typeof(TServiceType), parameterValues);
public object GetService([DN] Type serviceType) => getService(serviceType);
public object GetService([DN] Type serviceType, params ReadOnlySpan<object> parameterValues)
return getService(serviceType, parameterValues);
public object GetService([DN] string serviceTypeFullName)
=> GetService(serviceTypeFullName, Array.Empty<object>());
public object GetService([DN] string serviceTypeFullName, params ReadOnlySpan<object> parameterValues)
if(!tryGetType(serviceTypeFullName, out Type? serviceType)) throw Exceptions.TypeNotFound;
return GetService(serviceType, parameterValues);
public void Lock([O] object password) => (_isLocked, _password)=(true, password);
public void Unlock([O] object password)
_isLocked=password==_password;
if(_isLocked) throw Exceptions.IncorrectPassword;
private void checkLock() { if(_isLocked) throw Exceptions.Locked; }
private protected override void addService([DN] Dependency dependency)
base.addService(dependency);
private protected override void addServices([DN] params ReadOnlySpan<Dependency> dependencies)
base.addServices(dependencies);
private protected override bool tryAddService([DN] Dependency dependency)
return base.tryAddService(dependency);
private protected override bool tryAddServices([DN] params ReadOnlySpan<Dependency> dependencies)
return base.tryAddServices(dependencies);
public TypeResolverDelegate TypeResolver {get=>DependencyInversion.TypeResolver;set=>DependencyInversion.TypeResolver=value;}
public TypeNameResolverDelegate TypeNameResolver {get=>DependencyInversion.TypeNameResolver; set=>DependencyInversion.TypeNameResolver=value;}
public abstract class DependencyInversion([O] bool isDependencyInjection) : IEnumerable, IEnumerable<KeyValuePair<Type, ValidatedDependency>>
private protected bool IsDependencyInjection =isDependencyInjection;
private DependencyTypeFactoryDelegate _dependencyTypeFactory =>serviceType=>getService(serviceType)!;
private protected const string BACKING_PROPERTY_FIELD_NAME="__BACKINGFIELD";
private static AsyncLocal<TypeResolverDelegate> sa__typeResolver =new AsyncLocal<TypeResolverDelegate>();
private static AsyncLocal<TypeNameResolverDelegate> sa__typeNameResolver =new AsyncLocal<TypeNameResolverDelegate>();
private protected static DependencyOptions defaultOptions=new DependencyOptions();
private static Lazy<DependencyDictionary> sl_iocDictionary=new Lazy<DependencyDictionary>( ()=>new DependencyDictionary());
private static Lazy<DependencyDictionary> sl_diDictionary=new Lazy<DependencyDictionary>( ()=>new DependencyDictionary());
private static DependencyDictionary s_iocDictionary => sl_iocDictionary.Value;
private static DependencyDictionary s_diDictionary => sl_diDictionary.Value;
private DependencyDictionary _registrations => IsDependencyInjection ? s_diDictionary : s_iocDictionary;
static DependencyInversion()
=> ( sa__typeResolver.Value, sa__typeNameResolver.Value )=( type=>DefaultAddTypeResolver(type)!, type=>type.Name.Substring(1) );
public static TypeResolverDelegate TypeResolver {get=>sa__typeResolver.Value!;set=>sa__typeResolver.Value=value;}
public static TypeNameResolverDelegate TypeNameResolver {get=>sa__typeNameResolver.Value!;set=>sa__typeNameResolver.Value=value;}
public static Type DefaultAddTypeResolver([DN] Type serviceType,
TypeNameResolverDelegate? fromTypeToTargetTypeNameStringFunction=null)
if(fromTypeToTargetTypeNameStringFunction is not null) DependencyInversion.TypeNameResolver=fromTypeToTargetTypeNameStringFunction;
string toTypeName=string.Concat(DependencyInversion.TypeNameResolver?.Invoke(serviceType),"");
var toTypeFullName=serviceType.FullName!.Replace(serviceType.Name, toTypeName);
if(tryGetType(toTypeFullName, out Type? toType)) return toType;
if(toType is null) throw Exceptions.TypeNotFound;
#endregion STATIC MEMBERS
private protected virtual void addService([DN] Dependency dependency)
=> _registrations.AddDependency(dependency);
private protected void addService([DN] Type serviceType, [O] DependencyOptions options)
Type toType=resolveImplementationType(serviceType);
if(!serviceType.IsAbstract) options=options with {EnforceAbstractionToImplementationRelationship=false};
var dependency=new Dependency(serviceType:serviceType, implementationType:toType!, options);
_registrations.AddDependency(dependency);
private protected virtual void addServices([DN] ReadOnlySpan<Dependency> dependencies)
=> _registrations.AddDependencies(dependencies);
private object?[] createConstructorParametersDefaultValues([DN] TypeInfo typeInfo)
=> !typeInfo.HasConstructor ? Array.Empty<object>() : createParametersDefaultValues(parameters:new ReadOnlySpan<ParameterInfo>(typeInfo.ConstructorParameters as ParameterInfo[] ?? Array.Empty<ParameterInfo>()));
private object?[] createParametersDefaultValues(ReadOnlySpan<ParameterInfo> parameters)
var result=new object?[parameters.Length];
for(var c=0;c<parameters.Length;c++) result[c]=createParameterDefaultValue(parameters[c]);
private object? createParameterDefaultValue(ParameterInfo parameterInfo)
TypeInfo parameterTypeInfo=new TypeInfo(parameterInfo.ParameterType);
return parameterTypeInfo.IsAbstract ?
_dependencyTypeFactory?.Invoke(parameterTypeInfo.Type) :
Activator.CreateInstance(parameterTypeInfo.Type, createConstructorParametersDefaultValues(parameterTypeInfo!));
private object createType<TImplementationType>([O] ReadOnlySpan<object> parameterValues)
TypeInfo typeInfo=new TypeInfo<TImplementationType>();
return createType(typeInfo, parameterValues);
private object createType([DN] TypeInfo implementationTypeInfo, [O] ReadOnlySpan<object> parameterValues)
object? returned=default;
if(parameterValues.IsEmpty || parameterValues.Length==0)
parameterValues=createConstructorParametersDefaultValues(implementationTypeInfo)!;
returned=parameterValues.IsEmpty || parameterValues.Length==0 ?
Activator.CreateInstance(implementationTypeInfo.Type) :
Activator.CreateInstance(implementationTypeInfo.Type, parameterValues.ToArray());
private TServiceType getService<TServiceType>() => (TServiceType)getService(typeof(TServiceType))!;
private protected object getService([DN] Type serviceType, [O] ReadOnlySpan<object> parameterValues)
if(!tryFindRegisteredType(serviceType, out ValidatedDependency? validatedDependency)) throw Exceptions.TypeNotRegistered;
return getValidatedService(validatedDependency!, parameterValues)!;
private object getValidatedService([DN] ValidatedDependency validatedDependency, [O] ReadOnlySpan<object> parameterValues)
switch(validatedDependency!.Options.Lifetime)
case ObjectLifetime.Singleton: if(validatedDependency!.HasSingletonInstance) return validatedDependency.SingletonInstance; break;
case ObjectLifetime.ThreadScoped: if(validatedDependency!.HasThreadScopedInstance) return validatedDependency.ThreadScopedInstance; break;
case ObjectLifetime.FlowScoped: if(validatedDependency!.HasFlowScopedInstance) return validatedDependency.FlowScopedInstance; break;
if(validatedDependency.HasFactory)
Hi($"FACTORY {validatedDependency.ToString()}");
returned=validatedDependency.Options.Factory?.Invoke();
Hi($"NOT Factory {validatedDependency.ToString()}");
returned=createType(validatedDependency.ImplementationTypeInfo, parameterValues);
if(returned is null) throw Exceptions.TypeNotFound;
setProperties(returned!);
switch(validatedDependency!.Options.Lifetime)
case ObjectLifetime.Singleton: validatedDependency.SingletonInstance=returned!; break;
case ObjectLifetime.ThreadScoped: validatedDependency.ThreadScopedInstance=returned!; break;
case ObjectLifetime.FlowScoped: validatedDependency.FlowScopedInstance=returned!; break;
private protected Type resolveImplementationType(Type serviceType) => serviceType.IsAbstract || serviceType.IsInterface ? TypeResolver(serviceType) : serviceType;
private void setProperties([DN] object objectToSet)
from field in objectToSet.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
from rtype in _registrations.Values
let FieldType = field.FieldType
let currentValue = field.GetValue(objectToSet)
let ToType = rtype.ImplementationType
let IsNull = currentValue is null
let IsPrivate = field.IsPrivate
let IsPrivateProtected = field.IsFamilyAndAssembly
let IsProtectedInternal = field.IsFamilyOrAssembly
let IsInternal = field.IsAssembly
let IsPublic = field.IsPublic
let IsBackingProperty = field.Name.ToUpper().Contains(BACKING_PROPERTY_FIELD_NAME)
let IsCompilerGenerated = field.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) is not null
let FieldAccessibility = rtype.Options.FieldAccessibilityInjected
let HasFactory = rtype.Options.Factory is not null
let Factory = rtype.Options.Factory
FieldType.FullName==rtype.ServiceType.FullName
( IsPrivate && FieldAccessibility.HasFlag(Accessibility.Private))
|| ( IsPrivateProtected && FieldAccessibility.HasFlag(Accessibility.PrivateProtected))
|| ( IsProtectedInternal && FieldAccessibility.HasFlag(Accessibility.ProtectedInternal))
|| ( IsInternal && FieldAccessibility.HasFlag(Accessibility.Internal))
|| ( IsPublic && FieldAccessibility.HasFlag(Accessibility.Public))
select new {Field, ToType, HasFactory, Factory}
for(var c=0;c<properties.Length;c++)
properties[c].Field.SetValue(objectToSet, createType(new TypeInfo(properties[c].ToType)));
private protected virtual bool tryAddService([DN] Dependency dependency)
=> _registrations.TryAddDependency(dependency);
private protected bool tryAddService([DN] Type serviceType, [O, DPV(null)] DependencyOptions? options)
Type toType=this.resolveImplementationType(serviceType);
DependencyOptions o=options ?? defaultOptions;
if(!serviceType.IsAbstract)
o=o with {EnforceAbstractionToImplementationRelationship=false};
var dependency=new Dependency(serviceType:serviceType, implementationType:toType!, o);
return _registrations.TryAddDependency(dependency);
private protected virtual bool tryAddServices([DN] ReadOnlySpan<Dependency> dependencies)
=> _registrations.TryAddDependencies(dependencies);
private protected bool tryFindRegisteredType<TServiceType>(out ValidatedDependency? validatedDependency)
=> _registrations.TryGetValidatedDependency(typeof(TServiceType), out validatedDependency);
private protected bool tryFindRegisteredType([DN] Type serviceType, out ValidatedDependency? validatedDependency)
validatedDependency=_registrations[serviceType] ?? (IsDependencyInjection ? s_iocDictionary[serviceType] : s_diDictionary[serviceType]);
return validatedDependency is not null;
private protected static bool tryGetType([DN] string typeName,
[MaybeNullWhen(false)] [NotNullWhen(true)] out Type? target)
=> (target=Type.GetType(typeName, false, true)) is not null;
public bool IsRegistered([DAM(DAMT.PublicConstructors), DN] Type serviceType) => _registrations.ContainsDependency(serviceType);
public int Length => _registrations.Length;
public IEnumerator<KeyValuePair<Type, ValidatedDependency>> GetEnumerator() => _registrations.GetEnumerator();
IEnumerator<KeyValuePair<Type, ValidatedDependency>> IEnumerable<KeyValuePair<Type, ValidatedDependency>>.GetEnumerator()
=> _registrations.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _registrations.GetEnumerator();
public object? this[[DAM(DAMT.PublicConstructors), DN] Type serviceType]
if(!tryFindRegisteredType(serviceType, out ValidatedDependency? validatedDependency)) throw Exceptions.TypeNotFound;
return getValidatedService(validatedDependency!);
public class DependencyInversionAggregateException(params ReadOnlySpan<Exception> exceptions) : AggregateException(exceptions.ToArray())
public DependencyInversionAggregateException(AggregateException aggregateException) : this(new ReadOnlySpan<Exception>(aggregateException.InnerExceptions.ToArray())) {}
public DependencyInversionAggregateException(params IEnumerable<Exception> exceptions) : this(new ReadOnlySpan<Exception>(exceptions.ToArray())) {}
internal struct Exceptions
internal static Exception AliasNotFound => new ArgumentException("The alias name specified can not be found.");
internal static Exception AssignabilityViolation => new TypeAccessException("The source type can not be cast to the destination type, and EnforceAssignability flag is true; action prohibited.");
internal static Exception AbstractionToImplementationViolation
=> new TypeAccessException("The source type is not an abstraction (interface or abstract class), and EnforceAbstractionToImplementationRelationship flag is true; action prohibited.");
internal static Exception DuplicateAlias => new ArgumentException("The Alias specified is already used in another registration. Each alias must be unique to a specific single registration.");
internal static Exception DuplicateRegistration => new InvalidOperationException("The source type is already registered; duplicate registrations prohibited.");
internal static Exception IncorrectPassword => new ArgumentException("Incorrect password to unlock Dependency Injection component.");
internal static Exception IsNotClass => new InvalidOperationException("The destination type specific is not a class.");
internal static Exception IsValueType => new TypeAccessException("The destination type is a value type (struct); value types prohibited.");
internal static Exception IdenticalTypes => new InvalidOperationException("Both types are the same, and EnforceAbstractionToImplementationRelationship flag is true; action prohibited.");
internal static Exception Locked => new InvalidOperationException("The Dependency Inversion component is LOCKED. You can't add to it.");
internal static Exception TypeNotFound => new TypeAccessException("The type specified can not be found.");
internal static Exception TypeNotRegistered => new TypeAccessException("The type specified has not been registered for dependency inversion.");
internal static Exception Unknown => new ApplicationException("An unknown dependency inversion error occurred!!!");
internal static Exception CreateDependencyInversionAggregate(params ReadOnlySpan<Exception> exceptions)
=> new DependencyInversionAggregateException(exceptions);
internal static Exception CreateDependencyInversionAggregate(AggregateException aggregateException)
=> new DependencyInversionAggregateException(aggregateException);
internal static Exception CreateDependencyInversionAggregate(params IEnumerable<Exception> exceptions)
=> new DependencyInversionAggregateException(exceptions);
internal static Exception CreateAggregate(IEnumerable<Exception> exceptions)=>new AggregateException(exceptions);
public class AbstractTypeInfo<T>() : AbstractTypeInfo(typeof(T));
public class AbstractTypeInfo(Type type)
internal readonly Type Type=type;
internal readonly bool IsAbstract=type.IsAbstract;
internal readonly bool IsInterface=type.IsInterface;
internal readonly bool IsByRef=type.IsByRef;
public class TypeInfo<T>() : TypeInfo(typeof(T));
public class TypeInfo : AbstractTypeInfo
internal ConstructorInfo? Constructor;
internal ConstructorInfo[]? Constructors;
internal ParameterInfo?[]? ConstructorParameters;
internal string? Namespace;
internal bool HasConstructor, HasConstructorParameters;
private object?[]? _parameterValues;
private static ImmutableDictionary<Type, TypeInfo> _cache=ImmutableDictionary<Type, TypeInfo>.Empty;
public TypeInfo([DN] System.Type type) : base(type)
if(IsAbstract || IsInterface || IsByRef) return;
loadTypeInfo(_cache[type]);
_cache=_cache.Add(type,this);
void loadTypeInfo(TypeInfo? cachedTypeInfo=null)
if(cachedTypeInfo is null)
Constructors = Type.GetConstructors();
Constructor = Constructors?.First();
ConstructorParameters = Constructor?.GetParameters()?.ToArray();
Namespace = type?.FullName?.Replace(type.Name!, "");
HasConstructor = Constructor is not null;
HasConstructorParameters= ConstructorParameters is not null && ConstructorParameters.Length>0;
ref var c=ref cachedTypeInfo;
(Constructors, Constructor, Namespace, ConstructorParameters, HasConstructor, HasConstructorParameters) =
(c.Constructors, c.Constructor, c.Namespace, c.ConstructorParameters, c.HasConstructor, c.HasConstructorParameters);
internal ConstructorInfo GetConstructor(params Type [] types) => Type.GetConstructor(types);
internal bool IsCached => _cache.ContainsKey(Type);
public class Dependency<TServiceType, TImplementationType>() : Dependency(typeof(TServiceType), typeof(TImplementationType));
public class Dependency([DN] Type serviceType,
[DAM(DAMT.PublicConstructors)] [DN] Type implementationType)
[ThreadStatic, MaybeNull]
private static object _threadScopedInstance;
private static AsyncLocal<object> _flowScopedInstance=new();
private static object _singletonInstance;
internal DependencyOptions _options =new DependencyOptions();
internal Type ServiceType =serviceType;
[DAM(DAMT.PublicConstructors)]
internal Type ImplementationType =implementationType;
internal TypeInfo ServiceTypeInfo =new TypeInfo(serviceType);
internal TypeInfo ImplementationTypeInfo =new TypeInfo(implementationType);
public Dependency([DN] Type serviceType,
[DAM(DAMT.PublicConstructors)] [DN] Type implementationType,
DependencyOptions options=default) : this(serviceType, implementationType)
public Dependency([DN] Type serviceType,
[DAM(DAMT.PublicConstructors)] [DN] Type implementationType,
ObjectLifetime lifetime) : this(serviceType,implementationType)
=> (_options, FromHashCode)=(_options with {Lifetime=lifetime}, serviceType.GetHashCode());
public Dependency([DN] TypeInfo serviceTypeInfo,
[DAM(DAMT.PublicConstructors)] [DN] TypeInfo implementationTypeInfo,
ObjectLifetime lifetime) : this(serviceTypeInfo.Type, implementationTypeInfo.Type)
=> (_options, FromHashCode)=(_options with {Lifetime=lifetime}, serviceTypeInfo.Type.GetHashCode());
public DependencyOptions Options {get=>_options;}
internal object ThreadScopedInstance {get=>_threadScopedInstance;set=>_threadScopedInstance=value;}
internal object FlowScopedInstance {get=>_flowScopedInstance!.Value;set=>_flowScopedInstance!.Value=value;}
internal object SingletonInstance {get=>_singletonInstance;set=>_singletonInstance=value;}
internal ConstructorInfo Constructor => ImplementationType.GetConstructors()?.First()!;
[property: MaybeNull][AllowNull]
internal ParameterInfo[]? ConstructorParameters => ImplementationTypeInfo?.ConstructorParameters!;
internal bool HasSingletonInstance => SingletonInstance is not null;
internal bool HasThreadScopedInstance => ThreadScopedInstance is not null;
internal bool HasFlowScopedInstance => _flowScopedInstance?.Value is not null;
internal bool HasFactory => Options.Factory is not null;
internal bool HasConstructor => ImplementationTypeInfo.HasConstructor;
internal bool HasConstructorParameters => ImplementationTypeInfo.HasConstructorParameters;
public bool IsAssignable => ServiceType.IsAssignableFrom(ImplementationType) || (HasFactory && ServiceType.IsAssignableFrom(Options.Factory?.Method.ReturnType));
public int HashCode => ServiceType.GetHashCode();
public int FromHashCode {get;init;}
public override string ToString() =>$"ServiceType: {ServiceType}, ImplementationType: {ImplementationType}, hasCtor: {HasConstructor}, hasConstructorParameters: {HasConstructorParameters}, hasFactory: {HasFactory} ";
public sealed class ValidatedDependency(Dependency dependency) : Dependency(dependency.ServiceType, dependency.ImplementationType, dependency.Options);
public class UtilityMethods
public static bool Try(Action action)
public static bool TryCatch(Action action, out Exception? exception)
catch (Exception e) { exception=ExceptionDispatchInfo.Capture(e).SourceException; }
public static bool Try<T>(T input, Action<T> action)
where T : allows ref struct
=> Try(in input, action);
public static bool Try<T>(in T input, Action<T> action)
where T : allows ref struct
public static bool TryCatch<T>(T input, Action<T> action, out Exception? exception)
where T : allows ref struct
=> TryCatch(in input, action, out exception);
public static bool TryCatch<T>(in T input, Action<T> action, out Exception? exception)
where T : allows ref struct
catch (Exception e) { exception=ExceptionDispatchInfo.Capture(e).SourceException; }
public static bool Try<TResult>(Func<TResult> function, out TResult? output)
public static bool Try<T, TResult>(T input, Func<T, TResult> function, out TResult? output)
where T : allows ref struct
=> Try(in input, function, out output);
public static bool Try<T, TResult>(in T input, Func<T, TResult> function, out TResult? output)
where T : allows ref struct
{ output=function(input); }
public record struct CallerInfo(str Name="", int Line=0, str Path="")
public void Deconstruct(out str name, out int line, out str path)=>(name, line, path)=(Name, Line, Path);
public (string Key, object Value) [] ToArray() => [ ("Name", Name), ("Line", Line) , ("Path", Path) ];
public Exception UpdateExceptionData(Exception exception)
{ ( exception.Data["Name"], exception.Data["Line"], exception.Data["Path"] ) = ( Name, Line, Path); return exception; }
public Exception UpdateExceptionSource(Exception exception)
{ exception.Source=ToString(); return exception; }
public Exception UpdateException(Exception exception) => UpdateExceptionSource(UpdateExceptionData(exception));
public string ToString(string format="CallerInfo: {CallerName={Name}, CallerLineNumber={Line}, CallerFilePath={Path})")
=> format.Replace("{Name}", Name).Replace("{Line}", Line.ToString()).Replace("{Path}", Path);
public override string ToString()
=> ToString("CallerInfo: (CallerName={Name},CallerLineNumber={Line},CallerFilePath={Path})");
namespace Core.ErrorHandling.Exceptions.Custom
public class PebkacException(string message="Please inform the Help Desk that there's a problem in your chair.") : Exception(message);
public class Id10tException(string message="Please inform your boss that you keep getting an ID 10 T error, and that he might want to remedidate.") : Exception(message);
public class UnknownException([Optional] string message) : Exception(message);
public class PasswordException([Optional] string message) : Exception(message);
internal static class IdManager
private static int _lastId;
public static int GetId(ref int id)
newId = Interlocked.Increment(ref _lastId);
} while (newId.Equals(0));
Interlocked.CompareExchange(ref id, newId, 0);
namespace ExtensionMethods
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static System.MemoryExtensions;
public static class AsyncExtensions
public static Task<T> RunAsync<T>(this Func<T> f) => Task<T>.Factory.StartNew(f);
public static TaskAwaiter<T> GetAwaiter<T>(this T o)
var tcs=new TaskCompletionSource<T>();
return tcs.Task.GetAwaiter();
public static class ConditionalExtensions
public static void If(this bool condition, Action action)
{ if(condition) action(); }
public static class ConversionExtensions
public static T ChangeType<T>(this object val)
=> (T)Convert.ChangeType(val,typeof(T));
public static async IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IEnumerable<T> input)
{ foreach(var value in input) { yield return value; } }
public static IEnumerable<T> ToEnumerable<T>(this IAsyncEnumerable<T> asyncEnumerable)
=> (IEnumerable<T>)asyncEnumerable.ToBlockingEnumerable();
public static class DictionaryExtensions
private static SpinLock _lock;
public static TValue AddOrUpdate<TKey, TValue>(this Dictionary<TKey, TValue> data, in TKey key, Func<TKey, TValue> addFactory, Func<TKey,TValue,TValue> updateFactory, bool isThreadSafe=false)
if(!isThreadSafe) return doWork(ref data, key);
try { _lock.Enter(ref lockTaken); return doWork(ref data, key); }
finally { if (lockTaken) _lock.Exit(); }
TValue doWork(ref Dictionary<TKey, TValue> data, in TKey key)
ref var newOrOld=ref CollectionsMarshal.GetValueRefOrAddDefault<TKey, TValue>(data, key, out bool exists);
if(exists) newOrOld=updateFactory(key, newOrOld!); else newOrOld=addFactory(key);
public static TValue AddOrUpdate<TKey, TValue>(this Dictionary<TKey, TValue> data, in TKey key, TValue value, bool isThreadSafe=false)
=> DictionaryExtensions.AddOrUpdate<TKey, TValue>(data, key, key=>value, (key, old)=>value);
public static TValue AddOrUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> data, in TKey key, Func<TKey, TValue> addFactory, Func<TKey,TValue,TValue> updateFactory, bool isThreadSafe=false)
=> DictionaryExtensions.AddOrUpdate(data, key, addFactory, updateFactory, isThreadSafe);
public static TValue AddOrUpdate<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> data, in TKey key, TValue value)
=> DictionaryExtensions.AddOrUpdate(data:data, key:key, addFactory:(key)=>value, updateFactory:(key, old)=>value);
public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> [] data, TKey key, Func<TKey, TValue> addFactory, Func<TKey,TValue,TValue> updateFactory, bool isThreadSafe=false )
=> DictionaryExtensions.AddOrUpdate(data, key, addFactory, updateFactory, isThreadSafe);
public static TValue AddOrUpdate<TKey, TValue>(this KeyValuePair<TKey, TValue> [] data, TKey key, Func<TKey, TValue> addFactory, Func<TKey,TValue,TValue> updateFactory, bool isThreadSafe=false )
if(!isThreadSafe) return doWork(ref data, key);
try { _lock.Enter(ref lockTaken); return doWork(ref data, key); }
finally { if (lockTaken) _lock.Exit(); }
TValue doWork(ref KeyValuePair<TKey, TValue> [] data, TKey key)
var item=data.First(pair=>pair.Key.Equals(key));
var result=default(TValue);
if(!Unsafe.IsNullRef(ref item))
item=KeyValuePair.Create(key, result);
result=updateFactory(key,item.Value);
data.Append(KeyValuePair.Create(key, result));
public static TValue AddOrUpdate<TKey, TValue>(this KeyValuePair<TKey, TValue> [] data, TKey key, TValue value, bool isThreadSafe=false)
=> DictionaryExtensions.AddOrUpdate(data, key, key=>value, (key, old)=>value, isThreadSafe);
public static class EnumerableExtensions
public static int GetLength<T>([DN] this IEnumerable<T> e)
=> !Enumerable.TryGetNonEnumeratedCount(e, out int length) ? e.Count() : length;
public static int GetCount<T>([DN] this IEnumerable<T> e)
public static IEnumerable<T> Union<T>([DN] this IEnumerable<IEnumerable<T>?> enumerableOfEnumerables)
var result=new List<T>();
if(!Enumerable.TryGetNonEnumeratedCount(enumerableOfEnumerables, out int count)) count=enumerableOfEnumerables.Count();
result.AddRange(enumerableOfEnumerables?.ElementAt(c)!);
public static bool IsEmpty<T>([DN] this IEnumerable<T> e)
if(!e.TryGetNonEnumeratedCount(out int count)) count=e.Count();
public static void For<T>([DN]this Span<T> span, Action<T> action)
=> EnumerableExtensions.For(span, 0, span.Length, action);
public static void For<T>([DN]this ReadOnlySpan<T> span, Action<T> action)
=> EnumerableExtensions.For(span, 0, span.Length, action);
public static void For<T>([DN]this IEnumerable<T> e, Action<T> action)
if(!e.TryGetNonEnumeratedCount(out int count)) count=e.Count();
EnumerableExtensions.For<T>( e.ToArray().AsSpan(), 0, count, action);
public static ReadOnlySpan<TResult> For<T,TResult>([DN]this Span<T> span, Func<T,TResult> function)
=> EnumerableExtensions.For(span, 0, span.Length, function);
public static ReadOnlySpan<TResult> For<T,TResult>([DN]this ReadOnlySpan<T> span, Func<T,TResult> function)
=> EnumerableExtensions.For(span, 0, span.Length, function);
public static ReadOnlySpan<TResult> For<T, TResult>([DN]this IEnumerable<T> e, Func<T,TResult> function)
if(!e.TryGetNonEnumeratedCount(out int count)) count=e.Count();
return EnumerableExtensions.For(e.ToArray().AsSpan(), 0, count, function);
public static void For<T>([DN]this ReadOnlySpan<T> source, int minInclusive, int maxExclusive, Action<T> action)
if(source.IsEmpty) return;
ref var searchSpace=ref MemoryMarshal.GetReference(source);
for(var c=minInclusive;c<maxExclusive;c++)
var item=Unsafe.Add(ref searchSpace, c);
public static ReadOnlySpan<TResult> For<T, TResult>([DN]this ReadOnlySpan<T> source, int minInclusive, int maxExclusive, Func<T, TResult> function)
if(source.IsEmpty) return ReadOnlySpan<TResult>.Empty;
ref var searchSpace=ref MemoryMarshal.GetReference(source);
var results=new TResult [source.Length];
for(var c=minInclusive;c<maxExclusive;c++)
var item=Unsafe.Add(ref searchSpace, c);
results[c]=function(item);
return new ReadOnlySpan<TResult>(results);
public static class ExceptionExtensions
public static Exception AddData(this Exception e, params (string key, string value) [] kvp)
{ for(var c=0;c<kvp.Length;c++) e.Data[kvp[c].key]=kvp[c].value; return e; }
public static void ThrowIf(this Exception ex, Func<bool> validate, str message)
{ if(!validate()) throw new Exception(message); }
public static class MiscExtensions
public static void With<T>([DN] this T toThat, [DN] Action<T> doThis) => doThis?.Invoke(toThat);
public static TResult With<T, TResult>([DN] this T toThat, [DN] Func<T,TResult> doThis) => doThis.Invoke(toThat);
public static TResult Parse<T, TResult>([DN] this T fromThis, [DN]Func<T,TResult> function)=> function(fromThis);
public static class ReflectionExtensions
public static object?[] CreateConstructorParametersDefaultValues(this Type type, [Optional] Func<Type, object> abstractTypeCreationFactory, int constructorIndex=0)
var ctors=type.GetConstructors();
if(ctors is null) return Array.Empty<object>();
ParameterInfo [] ctorParameters=Array.Empty<ParameterInfo>();
if(ctors!=Array.Empty<ConstructorInfo>())
if(ctors.Length>=constructorIndex) ctor=ctors[constructorIndex]; else throw new ArgumentException("ConstructorIndex is out of range");
ctorParameters=ctor.GetParameters();
return ctorParameters.Length==0 ? Array.Empty<object>() : CreateParametersDefaultValues(parameters:ctorParameters, abstractTypeCreationFactory);
public static object?[] CreateMethodParametersDefaultValues([DN] this MethodInfo method, [Optional] Func<Type, object> abstractTypeCreationFactory)
var parameters=method.GetParameters();
if(!Enumerable.TryGetNonEnumeratedCount(parameters, out int count)) count=parameters.Count();
return parameters is null || count==0 ? Array.Empty<object>() : CreateParametersDefaultValues(parameters:parameters, abstractTypeCreationFactory);
public static object?[] CreateParametersDefaultValues(this ParameterInfo [] parameters, [Optional] Func<Type, object> abstractTypeCreationFactory)
if(parameters is null || parameters.Length==0) return Array.Empty<object>();
var returned=new object?[parameters.Length];
for(var c=0;c<parameters.Length;c++)
Type parameterType=parameters[c].ParameterType;
if (parameterType.IsAbstract && abstractTypeCreationFactory is not null)
returned[c]=abstractTypeCreationFactory?.Invoke(parameterType);
else if (!parameterType.IsAbstract)
returned[c]=System.Activator.CreateInstance(parameterType, parameterType.CreateConstructorParametersDefaultValues());
public static object? CreateType2<TInterface>(this Type implementationType, [Optional] Func<Type, object> abstractTypeCreationFactory, [Optional, DPV(0)] int constructorIndex, [Optional] params object [] parameterValues)
=> CreateType2(typeof(TInterface), implementationType, abstractTypeCreationFactory, constructorIndex, parameterValues);
public static object? CreateType2(this Type @interface, Type implementationType, [Optional] Func<Type, object> abstractTypeCreationFactory, [Optional, DPV(0)] int constructorIndex, [Optional] params object [] parameterValues)
if(!@interface.IsAbstract) throw new ArgumentException($"Parameter @interface ({@interface}) is not an abstract type.");
return CreateType2(implementationType, abstractTypeCreationFactory, constructorIndex, parameterValues);
public static object? CreateType2(this Type implementationType, [Optional] Func<Type, object> abstractTypeCreationFactory, [Optional, DPV(0)] int constructorIndex, [Optional] params object [] parameterValues)
object? returned=default;
if(parameterValues is null || parameterValues.Length==0)
returned=System.Activator.CreateInstance(implementationType, CreateConstructorParametersDefaultValues(implementationType, abstractTypeCreationFactory, constructorIndex));
else returned=System.Activator.CreateInstance(implementationType, parameterValues);
public static bool HasGenericInterface(this Type type, Type interf, Type typeparameter)
var result=type.GetInterfaces()
i=>i.IsGenericType && i.GetGenericTypeDefinition()==interf
i=>i.GetGenericArguments()[0]==typeparameter
foreach (Type i in type.GetInterfaces())
if (i.IsGenericType && i.GetGenericTypeDefinition() == interf)
if (i.GetGenericArguments()[0] == typeparameter)
public static bool HasGenericInterface(this Type type, Type interf, Type typeparameter, out Type foundInterface)
foundInterface=type.GetInterfaces()
i=>i.IsGenericType && i.GetGenericTypeDefinition()==interf
i=>i.GetGenericArguments()[0]==typeparameter
return Object.Equals(foundInterface,null);
public static bool HasInterface(this Type type, Type @interface) => type.GetInterfaces().Contains(@interface);
public static bool HasInterface(this object @object, Type @interface) => @object.GetType().GetInterfaces().Contains(@interface);
public static bool HasInterface<T>(Type @interface)
=> typeof(T).GetInterfaces().Contains(@interface);
public static bool TryGetType(string typeName, out Type target)
target=Type.GetType(typeName, false, true) ?? Type.Missing.GetType();
return target is not null;
public static int GetHashCodeSkeet(this object [] objs)
for(var c=0;c<objs.Length;c++)
hash = hash * 23 + objs[1].GetHashCode();
public static class StringExtensions
public static string Append([DN]this StringBuilder sb, params string [] strings)
{ StringExtensions.Append(sb, strings.AsSpan()); return sb.ToString(); }
public static string Append([DN]this StringBuilder sb, Span<string> strings)
{ EnumerableExtensions.For(strings,s=>sb.Append(s)); return sb.ToString(); }
public static class TypeExtensions
public static bool Switch<T>(this T toTest, Func<bool> [] conditions, params ReadOnlySpan<Action> actions)
var aArray=actions.ToArray();
for(var c=0;c<conditions.Length;c++)
if(conditions[c].Invoke())
public static bool Switch<T>(this T toTest, Func<T, bool> [] conditions, params ReadOnlySpan<Action> actions)
var aArray=actions.ToArray();
for(var c=0;c<conditions.Length;c++)
if(conditions[c].Invoke(toTest))
public static bool Switch<T, TResult>(this T toTest, out TResult? returned, Func<T, bool> [] conditions, params ReadOnlySpan<Func<TResult?>> functions)
returned=default(TResult);
var aArray=functions.ToArray();
for(var c=0;c<conditions.Length;c++)
if(conditions[c].Invoke(toTest))
returned=aArray[c].Invoke();
public static bool Switch<T, TResult>(this T toTest, out TResult? returned, Func<T, bool> [] conditions, params ReadOnlySpan<Func<T, TResult?>> functions)
returned=default(TResult);
var aArray=functions.ToArray();
for(var c=0;c<conditions.Length;c++)
if(conditions[c].Invoke(toTest))
returned=aArray[c].Invoke(toTest);
public static string ToFriendlyName([DN] this Type type)
Dictionary<string, string[]> replacements=new([
new KeyValuePair<string, string[]>("Expression", ["Expression1","Expression2","Expression3","Expression4","Expression5","Expression6","Expression7","Expression8","Expression9"])
var fn=type.FullName.AsSpan();
var indexOfBracket=fn.IndexOf('[');
var typeName=type.IsGenericType
? string.Concat(fn.Slice(0, fn.IndexOf('`')).ToString(), getGenericArgumentsString(type.GetGenericArguments()))
: (indexOfBracket==-1 ? fn.ToString() : fn.Slice(0, indexOfBracket).ToString());
string getGenericArgumentsString(Type[] genericArguments)
if(genericArguments?.Length==0) return string.Empty;
string result=string.Concat("<", string.Join(',', genericArguments.Select(g=>g.IsGenericType ? ToFriendlyName(g) : g.Name)), ">");
void replaceExceptionCases()
if(!Enumerable.TryGetNonEnumeratedCount(replacements, out int count)) count=replacements.Count();
var item=replacements.ElementAt(c);
for(var r=0;r<item.Value.Length;r++) typeName=typeName.Replace(item.Value[r], item.Key);
public static class ValidationExtensions
public static void Validate<T>(this T item, [DN]bool condition=true, str friendlyMessage=null, str friendlyParameterName=null, [CallerArgumentExpression("condition")] string? argumentMessage=null)
if (!condition) throw new ArgumentException(
string.Concat($"Argument failed validation: <{argumentMessage}>",
friendlyMessage is null ? string.Empty : $"\t{friendlyMessage}"),
friendlyParameterName is null ? string.Empty : friendlyParameterName);
public static class SpanMemoryExtensions
public static class Validation
public static void ValidateArgument([DN]string parameterName, [DN]bool validateState=true, str friendlyMessage=null, [CallerArgumentExpression("validateState")] string argumentMessage="")
if (!validateState) throw new ArgumentException(
string.Concat($"Argument failed validation: <{argumentMessage}>", friendlyMessage is null ? string.Empty : $"\t{friendlyMessage}"), parameterName);
public static void ValidateArgument(string parameterName, [DN] IEnumerable<bool> validateStates, str friendlyMessage=null)
ValidateArgument("IEnumerable<bool>", validateStates is not null, "Enumerable must not be null.");
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
ValidateArgument("IEnumerable<bool>", validateStatesCount>0, "Enumerable must not be empty.");
var conditionsSpan=CollectionsMarshal.AsSpan<bool>(validateStates.ToList());
ref var searchSpace=ref MemoryMarshal.GetReference(conditionsSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var condition=Unsafe.Add(ref searchSpace,i);
ValidateArgument(parameterName, condition, friendlyMessage);
public static void ValidateArgument(string parameterName, [DN] IEnumerable<bool> validateStates, [Optional] IEnumerable<str> friendlyMessages)
ValidateArgument("IEnumerable<bool>", validateStates is not null, "Enumerables must not be null.");
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
if(!friendlyMessages.TryGetNonEnumeratedCount(out int friendlyMessagesCount)) friendlyMessagesCount=friendlyMessages.Count();
ValidateArgument("IEnumerable<bool>", validateStatesCount>0, "Enumerables must not be empty.");
if(friendlyMessagesCount>0)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", validateStatesCount==friendlyMessagesCount, "Enumerables must have the same count.");
var conditionsSpan=CollectionsMarshal.AsSpan<bool>(validateStates.ToList());
var messagesSpan=CollectionsMarshal.AsSpan<str>(friendlyMessages.ToList());
ref var conditionsSpace=ref MemoryMarshal.GetReference(conditionsSpan);
ref var messagesSpace=ref MemoryMarshal.GetReference(messagesSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var condition=Unsafe.Add(ref conditionsSpace,i);
var message=Unsafe.Add(ref messagesSpace,i);
ValidateArgument(parameterName, condition, message);
public static void ValidateArguments([DN] IEnumerable<string> parameterNames,[DN] IEnumerable<bool> validateStates, [Optional] IEnumerable<str> friendlyMessages)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", parameterNames is not null && validateStates is not null && friendlyMessages is not null, "Enumerables must not be null.");
if(!parameterNames.TryGetNonEnumeratedCount(out int parameterNamesCount)) parameterNamesCount=parameterNames.Count();
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
if(!friendlyMessages.TryGetNonEnumeratedCount(out int friendlyMessagesCount)) friendlyMessagesCount=friendlyMessages.Count();
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", parameterNamesCount>0 && validateStatesCount>0 && friendlyMessagesCount>0, "Enumerables must not be empty.");
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", parameterNamesCount==validateStatesCount && parameterNamesCount==friendlyMessagesCount, "Enumerables must have the same count.");
if(friendlyMessagesCount>0)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", validateStatesCount==friendlyMessagesCount, "Enumerables must have the same count.");
var parametersSpan=CollectionsMarshal.AsSpan<str>(parameterNames.ToList());
var conditionsSpan=CollectionsMarshal.AsSpan<bool>(validateStates.ToList());
var messagesSpan=CollectionsMarshal.AsSpan<str>(friendlyMessages.ToList());
ref var parametersSpace=ref MemoryMarshal.GetReference(parametersSpan);
ref var conditionsSpace=ref MemoryMarshal.GetReference(conditionsSpan);
ref var messagesSpace=ref MemoryMarshal.GetReference(messagesSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var parameter=Unsafe.Add(ref parametersSpace,i);
var condition=Unsafe.Add(ref conditionsSpace,i);
var message=Unsafe.Add(ref messagesSpace,i);
ValidateArgument(parameter, condition, message);
public static void ValidateArgument(string parameterName, Func<bool> validateState, str friendlyMessage=null, [CallerArgumentExpression("validateState")] string? argumentMessage=null)
if(validateState is null) return;
if (!validateState()) throw new ArgumentException(
string.Concat($"Argument failed validation: <{argumentMessage}>", friendlyMessage is null ? string.Empty : $"\t{friendlyMessage}"), parameterName);
public static void ValidateArgument(string parameterName,[DN] IEnumerable<Func<bool>> validateStates, [Optional] str friendlyMessage)
ValidateArgument("IEnumerable<Func<bool>>", validateStates is not null, "Enumerables must not be null.");
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
ValidateArgument("IEnumerable<Func<bool>>", validateStatesCount>0, "Enumerables must not be empty.");
var conditionsSpan=CollectionsMarshal.AsSpan<Func<bool>>(validateStates.ToList());
ref var searchSpace=ref MemoryMarshal.GetReference(conditionsSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var condition=Unsafe.Add(ref searchSpace,i);
if(condition is null) continue;
ValidateArgument(parameterName, condition, friendlyMessage);
public static void ValidateArgument(string parameterName, [DN] IEnumerable<Func<bool>> validateStates, [Optional] IEnumerable<str> friendlyMessages)
ValidateArgument("IEnumerable<bool>", validateStates is not null, "Enumerables must not be null.");
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
if(!friendlyMessages.TryGetNonEnumeratedCount(out int friendlyMessagesCount)) friendlyMessagesCount=friendlyMessages.Count();
ValidateArgument("IEnumerable<bool>", validateStatesCount>0, "Enumerables must not be empty.");
if(friendlyMessagesCount>0)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", validateStatesCount==friendlyMessagesCount, "Enumerables must have the same count.");
var conditionsSpan=CollectionsMarshal.AsSpan<Func<bool>>(validateStates.ToList());
var messagesSpan=CollectionsMarshal.AsSpan<str>(friendlyMessages.ToList());
ref var conditionsSpace=ref MemoryMarshal.GetReference(conditionsSpan);
ref var messagesSpace=ref MemoryMarshal.GetReference(messagesSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var condition=Unsafe.Add(ref conditionsSpace,i);
if(condition is null) continue;
var message=Unsafe.Add(ref messagesSpace,i);
ValidateArgument(parameterName, condition, message);
public static void ValidateArguments([DN] IEnumerable<string> parameterNames, [DN] IEnumerable<Func<bool>> validateStates, [Optional] IEnumerable<str> friendlyMessages)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", parameterNames is not null && validateStates is not null && friendlyMessages is not null, "Enumerables must not be null.");
if(!parameterNames.TryGetNonEnumeratedCount(out int parameterNamesCount)) parameterNamesCount=parameterNames.Count();
if(!validateStates.TryGetNonEnumeratedCount(out int validateStatesCount)) validateStatesCount=validateStates.Count();
if(!friendlyMessages.TryGetNonEnumeratedCount(out int friendlyMessagesCount)) friendlyMessagesCount=friendlyMessages.Count();
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", parameterNamesCount>0 && validateStatesCount>0, "Enumerables must not be empty.");
if(friendlyMessagesCount>0)
ValidateArgument("IEnumerable<bool> and IEnumerable<str>", validateStatesCount==friendlyMessagesCount, "Enumerables must have the same count.");
var parametersSpan=CollectionsMarshal.AsSpan<str>(parameterNames.ToList());
var conditionsSpan=CollectionsMarshal.AsSpan<Func<bool>>(validateStates.ToList());
var messagesSpan=CollectionsMarshal.AsSpan<str>(friendlyMessages.ToList());
ref var parametersSpace=ref MemoryMarshal.GetReference(parametersSpan);
ref var conditionsSpace=ref MemoryMarshal.GetReference(conditionsSpan);
ref var messagesSpace=ref MemoryMarshal.GetReference(messagesSpan);
for(var i=0;i<conditionsSpan.Length;i++)
var condition=Unsafe.Add(ref conditionsSpace,i);
if(condition is null) continue;
var parameter=Unsafe.Add(ref parametersSpace,i);
var message=Unsafe.Add(ref messagesSpace,i);
ValidateArgument(parameter, condition, message);
public static class DebugMethods
public static int DebugCallCount=0;
public static bool debug=true;
public static void DoNothing() {}
public static void L() { if(debug) { Console.Write("\n");} }
public static void W<T>(T s) { if(debug) { Console.Write(s);} }
public static void WL<T>(T s) { if(debug) { Console.WriteLine(s);} }
public static void Hi(object message=null, [System.Runtime.CompilerServices.CallerMemberName] in str callerName="", [System.Runtime.CompilerServices.CallerLineNumber]in int callerLineNumber=0, [CallerFilePath]in str callerFilePath="")
Console.Write($"\tHI {DebugCallCount}>\t(Name: {callerName}\tLineNumber: {callerLineNumber}");
if(callerFilePath is not null && !string.IsNullOrWhiteSpace(callerFilePath + "")) Console.Write($"\tFile: {callerFilePath}");
if(message is not null && !string.IsNullOrWhiteSpace(message+"")) Console.Write($"\n\t\t\t\t--- {message}");
public static void Hi([DN] object [] messages, [System.Runtime.CompilerServices.CallerMemberName] in str callerName="", [System.Runtime.CompilerServices.CallerLineNumber]in int callerLineNumber=0, [CallerFilePath]in str callerFilePath="")
Console.Write($"\tHI {DebugCallCount}>\t(Name: {callerName}\tLineNumber: {callerLineNumber}");
if(callerFilePath is not null && !string.IsNullOrWhiteSpace(callerFilePath + "")) Console.Write($"\tFile: {callerFilePath}");
foreach(var message in messages)
if(message is not null && !string.IsNullOrWhiteSpace(message+"")) Console.Write($"\n\t\t\t\t--- {message}");