using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace MessagePipe { public partial class BuiltinContainerBuilder { public BuiltinContainerBuilder() { } public BuiltinContainerBuilder AddMessagePipe() { return AddMessagePipe(_ => { }); } public BuiltinContainerBuilder AddMessagePipe(Action configure) { ServiceCollectionExtensions.AddMessagePipe(this, configure); return this; } public IServiceProvider BuildServiceProvider() { return new BuiltinContainerBuilderServiceProvider(this); } /// Register IPublisher[TMessage] and ISubscriber[TMessage](includes Async/Buffered) to container builder. public BuiltinContainerBuilder AddMessageBroker() { var services = this; // keyless PubSub services.AddSingleton(typeof(MessageBrokerCore)); services.AddSingleton(typeof(IPublisher), typeof(MessageBroker)); services.AddSingleton(typeof(ISubscriber), typeof(MessageBroker)); // keyless PubSub async services.AddSingleton(typeof(AsyncMessageBrokerCore)); services.AddSingleton(typeof(IAsyncPublisher), typeof(AsyncMessageBroker)); services.AddSingleton(typeof(IAsyncSubscriber), typeof(AsyncMessageBroker)); // keyless buffered PubSub services.AddSingleton(typeof(BufferedMessageBrokerCore)); services.AddSingleton(typeof(IBufferedPublisher), typeof(BufferedMessageBroker)); services.AddSingleton(typeof(IBufferedSubscriber), typeof(BufferedMessageBroker)); // keyless buffered PubSub async services.AddSingleton(typeof(BufferedAsyncMessageBrokerCore)); services.AddSingleton(typeof(IBufferedAsyncPublisher), typeof(BufferedAsyncMessageBroker)); services.AddSingleton(typeof(IBufferedAsyncSubscriber), typeof(BufferedAsyncMessageBroker)); return this; } /// Register IPublisher[TKey, TMessage] and ISubscriber[TKey, TMessage](includes Async) to container builder. public BuiltinContainerBuilder AddMessageBroker() { var services = this; // keyed PubSub services.AddSingleton(typeof(MessageBrokerCore)); services.AddSingleton(typeof(IPublisher), typeof(MessageBroker)); services.AddSingleton(typeof(ISubscriber), typeof(MessageBroker)); // keyed PubSub async services.AddSingleton(typeof(AsyncMessageBrokerCore)); services.AddSingleton(typeof(IAsyncPublisher), typeof(AsyncMessageBroker)); services.AddSingleton(typeof(IAsyncSubscriber), typeof(AsyncMessageBroker)); return this; } /// Register IRequestHandler[TRequest, TResponse] to container builder. public BuiltinContainerBuilder AddRequestHandler() where THandler : IRequestHandler { var services = this; services.AddSingleton(typeof(IRequestHandlerCore), typeof(THandler)); services.AddSingleton(typeof(IRequestHandler), typeof(RequestHandler)); return this; } /// Register IAsyncRequestHandler[TRequest, TResponse] to container builder. public BuiltinContainerBuilder AddAsyncRequestHandler() where THandler : IAsyncRequestHandler { var services = this; services.AddSingleton(typeof(IAsyncRequestHandlerCore), typeof(THandler)); services.AddSingleton(typeof(IAsyncRequestHandler), typeof(AsyncRequestHandler)); AsyncRequestHandlerRegistory.Add(typeof(TRequest), typeof(TResponse), typeof(THandler)); return this; } public BuiltinContainerBuilder AddMessageHandlerFilter() where T : class, IMessageHandlerFilter { this.TryAddTransient(typeof(T)); return this; } public BuiltinContainerBuilder AddAsyncMessageHandlerFilter() where T : class, IAsyncMessageHandlerFilter { this.TryAddTransient(typeof(T)); return this; } public BuiltinContainerBuilder AddRequestHandlerFilter() where T : class, IRequestHandlerFilter { this.TryAddTransient(typeof(T)); return this; } public BuiltinContainerBuilder AddAsyncRequestHandlerFilter() where T : class, IAsyncRequestHandlerFilter { this.TryAddTransient(typeof(T)); return this; } } // DI Container builder. public partial class BuiltinContainerBuilder : IServiceCollection { internal readonly Dictionary singletonInstances = new Dictionary(); internal readonly List<(Type serviceType, Type implementationType)> singleton = new List<(Type serviceType, Type implementationType)>(); internal readonly List<(Type serviceType, Type implementationType)> transient = new List<(Type serviceType, Type implementationType)>(); public void AddSingleton(T instance) { singletonInstances[typeof(T)] = instance; } public void AddSingleton(Type type) { singleton.Add((type, type)); } public void AddTransient(Type type) { transient.Add((type, type)); } public void TryAddTransient(Type type) { foreach (var item in transient) { if (item.serviceType == type) { return; } } transient.Add((type, type)); } public void AddSingleton(Type serviceType, Type implementationType) { singleton.Add((serviceType, implementationType)); } public void Add(Type serviceType, Type implementationType, InstanceLifetime lifetime) { if (lifetime == InstanceLifetime.Scoped || lifetime == InstanceLifetime.Singleton) { singleton.Add((serviceType, implementationType)); } else // Transient { transient.Add((serviceType, implementationType)); } } } class BuiltinContainerBuilderServiceProvider : IServiceProvider { readonly Dictionary> singletonInstances; readonly Dictionary transientTypes; public BuiltinContainerBuilderServiceProvider(BuiltinContainerBuilder builder) { this.singletonInstances = new Dictionary>(builder.singletonInstances.Count + builder.singleton.Count); this.transientTypes = new Dictionary(builder.transient.Count); foreach (var item in builder.singletonInstances) { this.singletonInstances[item.Key] = new Lazy(() => item.Value); } foreach (var item in builder.singleton) { var implType = item.implementationType; this.singletonInstances[item.serviceType] = new Lazy(() => new ServiceProviderType(implType).Instantiate(this, 0)); // memo: require to lazy with parameter(pass depth). } foreach (var item in builder.transient) { this.transientTypes[item.serviceType] = new ServiceProviderType(item.implementationType); } } public object GetService(Type serviceType) { return GetService(serviceType, 0); } public object GetService(Type serviceType, int depth) { if (serviceType == typeof(IServiceProvider)) { return this; // resolve self } if (singletonInstances.TryGetValue(serviceType, out var value)) { return value.Value; // return Lazy.Value } if (transientTypes.TryGetValue(serviceType, out var providerType)) { return providerType.Instantiate(this, depth); } return null; } } class ServiceProviderType { readonly Type type; readonly ConstructorInfo ctor; readonly ParameterInfo[] parameters; public ServiceProviderType(Type type) { var info = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance) .Select(x => new { ctor = x, parameters = x.GetParameters() }) .OrderByDescending(x => x.parameters.Length) // MaxBy .FirstOrDefault(); if (!type.IsValueType && info == null) { throw new InvalidOperationException("ConsturoctorInfo is not found, is stripped? Type:" + type.FullName); } this.type = type; this.ctor = info?.ctor; this.parameters = info?.parameters; } public object Instantiate(BuiltinContainerBuilderServiceProvider provider, int depth) { if (ctor == null) { return Activator.CreateInstance(type); } if (parameters.Length == 0) { return ctor.Invoke(Array.Empty()); } if (depth > 15) { throw new InvalidOperationException("Parameter too recursively: " + type.FullName); } var p = new object[parameters.Length]; for (int i = 0; i < p.Length; i++) { p[i] = provider.GetService(parameters[i].ParameterType, depth + 1); } return ctor.Invoke(p); } } }