Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation and code-style fixes #83

Merged
merged 2 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/SteroidsDI.AspNetCore/AspNetCoreHttpScopeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

namespace SteroidsDI.AspNetCore;

/// <summary> <see cref="IScopeProvider"/> for ASP.NET Core working with <see cref="IHttpContextAccessor"/>. </summary>
/// <summary>
/// <see cref="IScopeProvider"/> for ASP.NET Core working with <see cref="IHttpContextAccessor"/>.
/// </summary>
public sealed class AspNetCoreHttpScopeProvider : IScopeProvider
{
/// <summary> Gets scoped <see cref="IServiceProvider" />, for the current HTTP request. </summary>
/// <summary> Gets scoped <see cref="IServiceProvider" /> for the current HTTP request. </summary>
/// <param name="rootProvider"> The root <see cref="IServiceProvider" /> object to obtain <see cref="IHttpContextAccessor"/>. </param>
/// <returns> The scoped <see cref="IServiceProvider" /> object or <c>null</c> if there is no current HTTP request. </returns>
public IServiceProvider? GetScopedServiceProvider(IServiceProvider rootProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ namespace Microsoft.Extensions.DependencyInjection;
/// <summary> Extension methods for <see cref="IServiceCollection"/>. </summary>
public static class ServiceCollectionExtensions
{
/// <summary> Register <see cref="AspNetCoreHttpScopeProvider" /> in DI as one of the possible implementations for <see cref="IScopeProvider" />. </summary>
/// <summary>
/// Register <see cref="AspNetCoreHttpScopeProvider" /> in DI as one of
/// the possible implementations for <see cref="IScopeProvider" />.
/// </summary>
/// <param name="services"> A collection of DI container services. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
/// <returns>
/// Reference to the passed object <paramref name="services" /> to be able to call methods in a chain.
/// </returns>
public static IServiceCollection AddHttpScope(this IServiceCollection services)
{
services.AddHttpContextAccessor();
Expand Down
10 changes: 5 additions & 5 deletions src/SteroidsDI.Core/Defer.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
namespace System; // yes, this is exactly the namespace by analogy with Func<T>

/// <summary>
/// An analogue of <see cref="Func{TResult}" /> for deferred resolving of the required object from a DI container.
/// Has a more explicit/simple API compared to Func. This type must be used for those constructor arguments
/// that requires deferred calculation of T.
/// An analogue of <see cref="Func{TResult}" /> for deferred resolving of the required object
/// from a DI container. Has a more explicit/simple API compared to Func. This type must be
/// used for those constructor arguments that require deferred calculation of T.
/// </summary>
/// <typeparam name="T"> Dependency type. </typeparam>
/// <typeparam name="T"> Type of dependency. </typeparam>
public abstract class Defer<T> : IDefer<T>
{
/// <summary> Get required dependency. </summary>
/// <summary> Gets required dependency. </summary>
public abstract T Value { get; }
}
6 changes: 3 additions & 3 deletions src/SteroidsDI.Core/GenericScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ namespace SteroidsDI.Core;
/// Works in conjunction with <see cref="IScopeProvider" />.
/// </summary>
/// <typeparam name="T">
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may set unique
/// closed type, thereby providing its own storage, to which only it will have access.
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may
/// set unique closed type, thereby providing its own storage, to which only it will have access.
/// </typeparam>
public static class GenericScope<T>
{
private static readonly AsyncLocal<IDisposable?> _currentScope = new AsyncLocal<IDisposable?>();
private static readonly AsyncLocal<IDisposable?> _currentScope = new();

/// <summary> Gets or sets the current scope. </summary>
public static IDisposable? CurrentScope
Expand Down
6 changes: 3 additions & 3 deletions src/SteroidsDI.Core/IDefer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ namespace System;
/// <summary>
/// Covariant interface for <see cref="Defer{T}" /> which is an analogue of <see cref="Func{TResult}" />
/// for deferred resolving of the required object from a DI container.
/// Has a more explicit/simple API compared to Func. This type must be used for those constructor arguments
/// that requires deferred calculation of T.
/// Has a more explicit/simple API compared to Func. This type must be used for
/// those constructor arguments that require deferred calculation of T.
/// </summary>
/// <typeparam name="T"> Dependency type. </typeparam>
/// <typeparam name="T"> Type of dependency. </typeparam>
public interface IDefer<out T>
{
/// <summary> Get required dependency. </summary>
Expand Down
22 changes: 16 additions & 6 deletions src/SteroidsDI.Core/IScopeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
namespace SteroidsDI.Core;

/// <summary>
/// Provider of scoped <see cref="IServiceProvider" />. The mission of this interface is not to CREATE something,
/// but to provide the required object, which was created one way or another before that. That is, it is an interface
/// for STORAGE of objects. This is its difference from <see cref = "IScopeFactory" />.
/// Provider of scoped <see cref="IServiceProvider" />. The mission of this interface is
/// not to CREATE something, but to provide the required object, which was created one way
/// or another before that. That is, it is an interface for STORAGE of objects. This is its
/// difference from <see cref = "IScopeFactory" />.
/// </summary>
public interface IScopeProvider
{
/// <summary> Gets scoped <see cref="IServiceProvider" />, that is, one in which scoped dependencies can be resolved. </summary>
/// <param name="rootProvider"> The root <see cref="IServiceProvider" /> object, which the provider may need in order to calculate the current scope. </param>
/// <returns> The scoped <see cref="IServiceProvider" /> object or <c>null</c> if the provider does not have the current scope. </returns>
/// <summary>
/// Gets scoped <see cref="IServiceProvider" />, that is,
/// one in which scoped dependencies can be resolved.
/// </summary>
/// <param name="rootProvider">
/// The root <see cref="IServiceProvider" /> object, which the
/// provider may need in order to calculate the current scope.
/// </param>
/// <returns>
/// The scoped <see cref="IServiceProvider" /> object or <c>null</c>
/// if the provider does not have the current scope.
/// </returns>
IServiceProvider? GetScopedServiceProvider(IServiceProvider rootProvider);
}
6 changes: 3 additions & 3 deletions src/SteroidsDI.Core/Scoped.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
namespace SteroidsDI.Core;

/// <summary>
/// Auxiliary class for creating and destroying scopes. These are sections of code within which
/// Auxiliary class for creating and destroying scopes. Scopes are sections of code within which
/// scoped dependencies are resolved. This class controls the state of <see cref="GenericScope{T}" />,
/// initializing its initial state and cleaning/destroying it at the exit.
/// </summary>
/// <typeparam name="T">
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may set unique
/// closed type, thereby providing its own storage, to which only it will have access.
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may
/// set unique closed type, thereby providing its own storage, to which only it will have access.
/// </typeparam>
public struct Scoped<T> : IDisposable
{
Expand Down
6 changes: 3 additions & 3 deletions src/SteroidsDI.Tests/Cases/AllowRootProviderResolveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public void Should_Throw_When_No_Scopes_And_AllowRootProviderResolve_Disabled()
using (var scope = provider.CreateScope())
{
var service = scope.ServiceProvider.GetService<Service>()!;
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"The current scope is missing. Unable to get object of type 'ScopedAsSingleton' from the root provider.
Be sure to add the required provider (IScopeProvider) to the container using the TryAddEnumerable method or a special method from your transport library.
An object can be obtained from the root provider if it has a non-scoped lifetime and the parameter 'ServiceProviderAdvancedOptions.AllowRootProviderResolve' = true.");
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"The current scope is missing. Unable to resolve service 'ScopedAsSingleton' from the root service provider.
Be sure to add the required provider (IScopeProvider) to the DI container by using appropriate extension method.
Note that a service can be obtained from the root service provider only if it has a non-scoped lifetime (i.e. singleton or transient) and 'ServiceProviderAdvancedOptions.AllowRootProviderResolve' option is enabled.");
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/SteroidsDI.Tests/Cases/ValidateParallelScopesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public void Should_Throw_When_No_Scopes()
using (var scope = provider.CreateScope())
{
var service = scope.ServiceProvider.GetService<Service>()!;
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"An error occurred while resolving the type 'Scoped'
The type is declared as scoped within the context of the request, but an attempt to resolve the type is made outside the context of the request.
An application can simultaneously have several entry points that initialize their request contexts.
Be sure to add the required provider (IScopeProvider) to the container using the TryAddEnumerable method.");
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"An error occurred while resolving service 'Scoped'.
The service was registered as scoped, but an attempt to resolve this service is made outside of any scope.
An application can simultaneously have several entry points that initialize their scopes.
Be sure to add the required provider (IScopeProvider) to the DI container by using appropriate extension method.");
}
}
}
Expand Down Expand Up @@ -126,8 +126,8 @@ public void Should_Throw_When_More_Than_One_Scope_And_ValidateParallelScopes_Ena
using (var scope = provider.CreateScope())
{
var service = scope.ServiceProvider.GetService<Service>()!;
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"When 'ServiceProviderAdvancedOptions.ValidateParallelScopes' option is turned on, the simultaneous existence of several scopes from different providers was detected.
Scopes obtained from the following providers: SteroidsDI.GenericScopeProvider`1[SteroidsDI.Tests.Cases.ValidateParallelScopesTests+B], SteroidsDI.GenericScopeProvider`1[SteroidsDI.Tests.Cases.ValidateParallelScopesTests+A]");
Should.Throw<InvalidOperationException>(() => service.Scoped.Value).Message.ShouldBe(@"'ServiceProviderAdvancedOptions.ValidateParallelScopes' option is ON. The simultaneous existence of several scopes from different providers was detected.
Scopes were obtained from the following providers: SteroidsDI.GenericScopeProvider`1[SteroidsDI.Tests.Cases.ValidateParallelScopesTests+B], SteroidsDI.GenericScopeProvider`1[SteroidsDI.Tests.Cases.ValidateParallelScopesTests+A]");
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/SteroidsDI/DelegatedDefer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ namespace SteroidsDI;

internal sealed class DelegatedDefer<T> : Defer<T>
{
private readonly IServiceProvider _provider;
private readonly IServiceProvider _rootProvider;
private readonly ServiceProviderAdvancedOptions _options;

public DelegatedDefer(IServiceProvider provider, IOptions<ServiceProviderAdvancedOptions> options)
public DelegatedDefer(IServiceProvider rootProvider, IOptions<ServiceProviderAdvancedOptions> options)
{
_provider = provider;
_rootProvider = rootProvider;
_options = options.Value;
}

public override T Value => _provider.Resolve<T>(_options);
/// <inheritdoc/>
public override T Value => _rootProvider.Resolve<T>(_options);
}
40 changes: 27 additions & 13 deletions src/SteroidsDI/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ private static IServiceCollection AddAdvancedOptions(this IServiceCollection ser
public static BindingContext<TService> For<TService>(this IServiceCollection services)
where TService : class => new BindingContext<TService>(services);

/// <summary> Add the specified type <paramref name="factoryType" /> to the DI container as a factory that performs factory methods for creating objects. </summary>
/// <summary>
/// Adds the specified type <paramref name="factoryType" /> to the DI container
/// as a factory that performs factory methods for creating objects.
/// </summary>
/// <param name="services"> A collection of DI container services. </param>
/// <param name="factoryType"> Factory type. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
Expand All @@ -32,27 +35,32 @@ public static IServiceCollection AddFactory(this IServiceCollection services, Ty
return services;
}

/// <summary> Add the specified type <typeparamref name="TFactory"/> to the DI container as a factory that performs factory methods for creating objects. </summary>
/// <summary>
/// Adds the specified type <typeparamref name="TFactory"/> to the DI container
/// as a factory that performs factory methods for creating objects.
/// </summary>
/// <typeparam name="TFactory"> Factory type. </typeparam>
/// <param name="services"> A collection of DI container services. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
public static IServiceCollection AddFactory<TFactory>(this IServiceCollection services)
=> services.AddAdvancedOptions().AddFactory(typeof(TFactory));

/// <summary>
/// Register the factory <see cref="Func {TService}" /> to create an object of type <typeparamref name="TService" />.
/// This factory can find/select the correct scope (if one exists at all) through which you need to get the required object.
/// Differs from <see cref="AddDefer(IServiceCollection)" >AddDefer</see > by the fact that it works for only one specified
/// type, that is, this method may need to be called several times.
/// Registers the factory <see cref="Func {TService}" /> to create an object of type
/// <typeparamref name="TService" />. This factory can find/select the correct scope
/// (if one exists at all) through which you need to get the required object. Differs
/// from <see cref="AddDefer(IServiceCollection)" >AddDefer</see > by the fact that
/// it works for only one specified type, that is, this method may need to be called
/// several times.
/// </summary>
/// <typeparam name="TService"> Service type. </typeparam>
/// <param name="services"> A collection of DI container services. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
public static IServiceCollection AddFunc<TService>(this IServiceCollection services)
=> services.AddAdvancedOptions().AddSingleton(provider =>
=> services.AddAdvancedOptions().AddSingleton(rootProvider =>
{
var options = provider.GetRequiredService<IOptions<ServiceProviderAdvancedOptions>>();
return new Func<TService>(() => provider.Resolve<TService>(options.Value));
var options = rootProvider.GetRequiredService<IOptions<ServiceProviderAdvancedOptions>>();
return new Func<TService>(() => rootProvider.Resolve<TService>(options.Value));
});

/// <summary>
Expand All @@ -68,10 +76,13 @@ public static IServiceCollection AddDefer(this IServiceCollection services)
.AddSingleton(typeof(Defer<>), typeof(DelegatedDefer<>))
.AddSingleton(typeof(IDefer<>), typeof(DelegatedDefer<>));

/// <summary> Register <see cref="GenericScopeProvider{T}" /> in DI as one of the possible implementations of <see cref="IScopeProvider" />. </summary>
/// <summary>
/// Registers <see cref="GenericScopeProvider{T}" /> in DI as one of
sungam3r marked this conversation as resolved.
Show resolved Hide resolved
/// the possible implementations of <see cref="IScopeProvider" />.
/// </summary>
/// <typeparam name="T">
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may set unique
/// closed type, thereby providing its own storage, to which only it will have access.
/// An arbitrary type that is used to create various static AsyncLocal fields. The caller may
/// set unique closed type, thereby providing its own storage, to which only it will have access.
/// </typeparam>
/// <param name="services"> A collection of DI container services. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
Expand All @@ -82,7 +93,10 @@ public static IServiceCollection AddGenericScope<T>(this IServiceCollection serv
return services;
}

/// <summary> Register <see cref="MicrosoftScopeFactory" /> in DI as one of the possible the implementations of <see cref="IScopeFactory" />. </summary>
/// <summary>
/// Registers <see cref="MicrosoftScopeFactory" /> in DI as one of
/// the possible implementations of <see cref="IScopeFactory" />.
/// </summary>
/// <param name="services"> A collection of DI container services. </param>
/// <returns> Reference to the passed object <paramref name="services" /> to be able to call methods in a chain. </returns>
public static IServiceCollection AddMicrosoftScopeFactory(this IServiceCollection services)
Expand Down
11 changes: 7 additions & 4 deletions src/SteroidsDI/Factory/BindingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace SteroidsDI;

/// <summary>
/// Binding context for type <typeparamref name="TService" />. Currently serving named bindings, i.e. the context is
/// only the binding name, but may add additional context in the future.
/// Binding context for type <typeparamref name="TService" />. Currently serving named bindings,
/// i.e. the context is only the binding name, but may add additional context in the future.
/// </summary>
/// <typeparam name="TService"> The service type which context is customized. </typeparam>
public sealed class BindingContext<TService>
Expand All @@ -22,7 +22,10 @@ public BindingContext(IServiceCollection services)
/// <summary> A collection of DI container services. </summary>
public IServiceCollection Services { get; }

/// <summary> Register a named binding from type <typeparamref name="TService" /> to type <typeparamref name="TImplementation" />.</summary>
/// <summary>
/// Registers a named binding from type <typeparamref name="TService" />
/// to type <typeparamref name="TImplementation" />.
/// </summary>
/// <typeparam name="TImplementation"> Implementation type. </typeparam>
/// <param name="name">
/// The name of the binding. The name of the binding can be not only a string, but an arbitrary object.
Expand Down Expand Up @@ -52,7 +55,7 @@ public BindingContext<TService> Named<TImplementation>(object name, ServiceLifet
}

/// <summary>
/// Register a named binding from type <typeparamref name="TService" /> to type <typeparamref name="TImplementation" />
/// Registers a named binding from type <typeparamref name="TService" /> to type <typeparamref name="TImplementation" />
/// with a lifetime equal to the lifetime of the object from the default binding.
/// </summary>
/// <typeparam name="TImplementation">Implementation type. </typeparam>
Expand Down
4 changes: 2 additions & 2 deletions src/SteroidsDI/Factory/FactoryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
namespace SteroidsDI;

/// <summary>
/// The class generates a factory using the specified factory interface. The factory implementation delegates
/// resolve of objects to the appropriate <see cref="IServiceProvider" />.
/// This class generates a factory using the specified factory interface. The factory implementation
/// delegates resolve of objects to the appropriate <see cref="IServiceProvider" />.
/// </summary>
/// <remarks> See the manually written IMegaFactory_Generated example class in the test assembly. </remarks>
internal static class FactoryGenerator
Expand Down
Loading