// // ServiceCollectionExtensions.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Net; using Microsoft.Extensions.DependencyInjection; using NosSmooth.Comms.Core.NamedPipes; using NosSmooth.Comms.Core.Responders; using NosSmooth.Comms.Core.Tcp; using NosSmooth.Comms.Data; using NosSmooth.Comms.Data.Responders; using NosSmooth.Core.Client; namespace NosSmooth.Comms.Core.Extensions; /// /// Extension methods for . /// public static class ServiceCollectionExtensions { /// /// Adds server handling ( and ). /// /// /// The specific server has to be added separately as . /// /// The service collection. /// The same service collection. public static IServiceCollection AddServerHandling(this IServiceCollection serviceCollection) => serviceCollection .AddNosSmoothResolverOptions() .AddSingleton(p => new MessageHandler(p, true)) .AddSingleton() .AddInjecting(); /// /// Adds handling for a single client. /// /// /// The specific client has to be added separately as . /// /// The service collection. /// The same service collection. public static IServiceCollection AddSingleClientHandling(this IServiceCollection serviceCollection) => serviceCollection .AddInjecting() .AddNosSmoothResolverOptions() .AddMessageResponder() .AddSingleton() .AddSingleton(p => p.GetRequiredService()) .AddSingleton(p => new MessageHandler(p, false)) .AddScoped() .AddScoped(); /// /// Add handling for multiple clients. /// /// /// The clients should not be inside of the provider. /// Initialize clients outside of the provider and use the /// provider for injecting connection handler and nostale client. /// Nostale client will be created automatically if connection is injected successfully. /// Connection will be injected when calling message handler with the specific connection. /// /// Connection may be injected by setting properties in a scope. /// /// The service collection. /// The same service collection. public static IServiceCollection AddMultiClientHandling(this IServiceCollection serviceCollection) => serviceCollection .AddNosSmoothResolverOptions() .AddSingleton() .AddSingleton(p => new MessageHandler(p, false)) .AddInjecting() .AddScoped (p => p.GetRequiredService().Resolve(p.GetRequiredService())); /// /// Add with default NosSmooth options. /// /// The service collection. /// The same service collection. public static IServiceCollection AddNosSmoothResolverOptions(this IServiceCollection serviceCollection) => serviceCollection .Configure (o => o.Options = o.Options.WithResolver(NosSmoothResolver.Instance)); /// /// Adds a message responder. /// /// The service collection. /// The type of the responder. /// The same service collection. public static IServiceCollection AddMessageResponder(this IServiceCollection serviceCollection) { return serviceCollection.AddMessageResponder(typeof(TResponder)); } /// /// Adds a message responder. /// /// The service collection. /// The type of the responder. /// The same service collection. public static IServiceCollection AddMessageResponder(this IServiceCollection serviceCollection, Type responderType) { if (serviceCollection.Any(x => x.ImplementationType == responderType)) { // already added... assuming every packet responder was added even though that may not be the case. return serviceCollection; } if (!responderType.GetInterfaces().Any ( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMessageResponder<>) )) { throw new ArgumentException ( $"{nameof(responderType)} should implement IMessageResponder.", nameof(responderType) ); } var responderTypeInterfaces = responderType.GetInterfaces(); var responderInterfaces = responderTypeInterfaces.Where ( r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(IMessageResponder<>) ); foreach (var responderInterface in responderInterfaces) { serviceCollection.AddScoped(responderInterface, responderType); } return serviceCollection; } /// /// Adds a named pipe client as a . /// /// The service collection. /// A function for resolving the name of the pipe using service provider. /// The same service collection. public static IServiceCollection AddNamedPipeClient (this IServiceCollection serviceCollection, Func pipeNameResolver) => serviceCollection .AddSingleton(p => new NamedPipeClient(pipeNameResolver(p))) .AddSingleton(p => p.GetRequiredService()); /// /// Adds a named pipe server as a . /// /// The service collection. /// A function for resolving the name of the pipe using service provider. /// The same service collection. public static IServiceCollection AddNamedPipeServer (this IServiceCollection serviceCollection, Func pipeNameResolver) => serviceCollection .AddSingleton(p => new NamedPipeServer(pipeNameResolver(p))) .AddSingleton(p => p.GetRequiredService()); /// /// Adds tcp server as . /// /// The service collection. /// A resovler used for resolving the ip and port to bind to. /// The same service collection. public static IServiceCollection AddTcpServer ( this IServiceCollection serviceCollection, Func bindResolver ) { return serviceCollection .AddSingleton(p => p.GetRequiredService()) .AddSingleton ( p => { var resolved = bindResolver(p); return new TcpServer(resolved.BindAddress, resolved.Port); } ); } /// /// Adds tcp client as . /// /// The service collection. /// A resovler used for resolving the hostname and port to connect to. /// The same service collection. public static IServiceCollection AddTcpClient ( this IServiceCollection serviceCollection, Func connectionResolver ) { return serviceCollection .AddSingleton(p => p.GetRequiredService()) .AddSingleton ( p => { var resolved = connectionResolver(p); return new TcpClient(resolved.Hostname, resolved.Port); } ); } private static IServiceCollection AddInjecting(this IServiceCollection serviceCollection) => serviceCollection .AddScoped() .AddScoped ( p => { var handler = p.GetRequiredService().ConnectionHandler; if (handler is null) { throw new InvalidOperationException("Connection handler was requested, but is not injected."); } return handler; } ) .AddScoped ( p => { var connection = p.GetRequiredService().Connection; if (connection is null) { throw new InvalidOperationException("Connection was requested, but is not injected."); } return connection; } ); }