diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..23c94c9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,166 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = true:error +dotnet_style_qualification_for_property = true:error +dotnet_style_qualification_for_method = true:error +dotnet_style_qualification_for_event = true:error +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion +dotnet_code_quality_unused_parameters = all:error +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:error +csharp_prefer_simple_using_statement = true:error +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_braces = false +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:error +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:error +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_prefer_parameter_null_checking = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/Slim.Integration/Slim.Integration.csproj b/Slim.Integration/Slim.Integration.csproj new file mode 100644 index 0000000..a3089af --- /dev/null +++ b/Slim.Integration/Slim.Integration.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + true + latest + Alexandru Macocian + + + Integration with Microsoft.DependencyInjection library. + 1.0.0 + 1.0.0 + https://github.com/AlexMacocian/Slim + 1.0.0 + true + true + LICENSE + + + + + + + + diff --git a/Slim.Tests/MemoryLeakTests.cs b/Slim.Tests/MemoryLeakTests.cs index 8a5b7ab..ae5c51e 100644 --- a/Slim.Tests/MemoryLeakTests.cs +++ b/Slim.Tests/MemoryLeakTests.cs @@ -4,42 +4,41 @@ using Slim.Tests.Models; using System; using System.Threading.Tasks; -namespace Slim.Tests +namespace Slim.Tests; + +[TestClass] +public sealed class MemoryLeakTests { - [TestClass] - public sealed class MemoryLeakTests + private const int ScopesCount = 10000; + + [TestMethod] + public async Task CreateAndReleaseScopesDisposeManagerGCCollects() { - private const int ScopesCount = 10000; + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterScoped(); + var independentService = di.GetService(); + var dependentService = di.GetService(); - [TestMethod] - public async Task CreateAndReleaseScopesDisposeManagerGCCollects() + var weakRefs = new WeakReference[ScopesCount]; + for (int i = 0; i < ScopesCount; i++) { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterScoped(); - var independentService = di.GetService(); - var dependentService = di.GetService(); + var scopedDi = di.CreateScope(); + weakRefs[i] = new WeakReference(scopedDi); + var scopedDependentService = scopedDi.GetService(); + scopedDependentService.Should().NotBeNull(); + scopedDependentService.Should().NotBe(dependentService); + scopedDependentService.IndependentService.Should().NotBeNull(); + scopedDependentService.IndependentService.Should().Be(independentService); + } - var weakRefs = new WeakReference[ScopesCount]; - for (int i = 0; i < ScopesCount; i++) - { - var scopedDi = di.CreateScope(); - weakRefs[i] = new WeakReference(scopedDi); - var scopedDependentService = scopedDi.GetService(); - scopedDependentService.Should().NotBeNull(); - scopedDependentService.Should().NotBe(dependentService); - scopedDependentService.IndependentService.Should().NotBeNull(); - scopedDependentService.IndependentService.Should().Be(independentService); - } - - await Task.Delay(1500); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - foreach(var weakRef in weakRefs) - { - weakRef.IsAlive.Should().BeFalse(); - } + await Task.Delay(1500); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + foreach (var weakRef in weakRefs) + { + weakRef.IsAlive.Should().BeFalse(); } } } diff --git a/Slim.Tests/Models/DependentOnPrivateCtrService.cs b/Slim.Tests/Models/DependentOnPrivateCtrService.cs index f09e222..d084b94 100644 --- a/Slim.Tests/Models/DependentOnPrivateCtrService.cs +++ b/Slim.Tests/Models/DependentOnPrivateCtrService.cs @@ -1,13 +1,12 @@ -namespace Slim.Tests.Models -{ - public sealed class DependentOnPrivateCtrService : IDependentOnPrivateCtrService - { - private readonly IPrivateConstructorService privateConstructorService; +namespace Slim.Tests.Models; - public DependentOnPrivateCtrService( - IPrivateConstructorService privateConstructorService) - { - this.privateConstructorService = privateConstructorService; - } +public sealed class DependentOnPrivateCtrService : IDependentOnPrivateCtrService +{ + private readonly IPrivateConstructorService privateConstructorService; + + public DependentOnPrivateCtrService( + IPrivateConstructorService privateConstructorService) + { + this.privateConstructorService = privateConstructorService; } } diff --git a/Slim.Tests/Models/DependentOnServiceManagerService.cs b/Slim.Tests/Models/DependentOnServiceManagerService.cs index c91e273..e3be426 100644 --- a/Slim.Tests/Models/DependentOnServiceManagerService.cs +++ b/Slim.Tests/Models/DependentOnServiceManagerService.cs @@ -1,19 +1,18 @@ -namespace Slim.Tests.Models -{ - public class DependentOnServiceManagerService : IDependentOnServiceManagerService - { - public IServiceManager ServiceManager { get; } - public IServiceProducer ServiceProducer { get; } - public IServiceProvider ServiceProvider { get; } +namespace Slim.Tests.Models; - public DependentOnServiceManagerService( - IServiceManager serviceManager, - IServiceProvider serviceProvider, - IServiceProducer serviceProducer) - { - this.ServiceManager = serviceManager; - this.ServiceProvider = serviceProvider; - this.ServiceProducer = serviceProducer; - } +public class DependentOnServiceManagerService : IDependentOnServiceManagerService +{ + public IServiceManager ServiceManager { get; } + public IServiceProducer ServiceProducer { get; } + public IServiceProvider ServiceProvider { get; } + + public DependentOnServiceManagerService( + IServiceManager serviceManager, + IServiceProvider serviceProvider, + IServiceProducer serviceProducer) + { + this.ServiceManager = serviceManager; + this.ServiceProvider = serviceProvider; + this.ServiceProducer = serviceProducer; } } diff --git a/Slim.Tests/Models/DependentOnThrowingService.cs b/Slim.Tests/Models/DependentOnThrowingService.cs index 26f6b24..cf2095c 100644 --- a/Slim.Tests/Models/DependentOnThrowingService.cs +++ b/Slim.Tests/Models/DependentOnThrowingService.cs @@ -1,12 +1,11 @@ -namespace Slim.Tests.Models -{ - public sealed class DependentOnThrowingService : IDependentOnThrowingService - { - private readonly IThrowingService throwingService; +namespace Slim.Tests.Models; - public DependentOnThrowingService(IThrowingService throwingService) - { - this.throwingService = throwingService; - } +public sealed class DependentOnThrowingService : IDependentOnThrowingService +{ + private readonly IThrowingService throwingService; + + public DependentOnThrowingService(IThrowingService throwingService) + { + this.throwingService = throwingService; } } diff --git a/Slim.Tests/Models/DependentService.cs b/Slim.Tests/Models/DependentService.cs index e06d01c..ab39182 100644 --- a/Slim.Tests/Models/DependentService.cs +++ b/Slim.Tests/Models/DependentService.cs @@ -1,12 +1,11 @@ -namespace Slim.Tests.Models -{ - public class DependentService : IDependentService - { - public DependentService(IIndependentService independentService) - { - this.IndependentService = independentService; - } +namespace Slim.Tests.Models; - public IIndependentService IndependentService { get; } +public class DependentService : IDependentService +{ + public DependentService(IIndependentService independentService) + { + this.IndependentService = independentService; } + + public IIndependentService IndependentService { get; } } diff --git a/Slim.Tests/Models/DependentService2.cs b/Slim.Tests/Models/DependentService2.cs index 4339803..e294161 100644 --- a/Slim.Tests/Models/DependentService2.cs +++ b/Slim.Tests/Models/DependentService2.cs @@ -1,12 +1,11 @@ -namespace Slim.Tests.Models -{ - public sealed class DependentService2 : IDependentService2 - { - public IndependentService IndependentService { get; } +namespace Slim.Tests.Models; - public DependentService2(IndependentService independentService) - { - this.IndependentService = independentService; - } +public sealed class DependentService2 : IDependentService2 +{ + public IndependentService IndependentService { get; } + + public DependentService2(IndependentService independentService) + { + this.IndependentService = independentService; } } diff --git a/Slim.Tests/Models/DoNotInjectConstructorService.cs b/Slim.Tests/Models/DoNotInjectConstructorService.cs index 9637c9b..065fa04 100644 --- a/Slim.Tests/Models/DoNotInjectConstructorService.cs +++ b/Slim.Tests/Models/DoNotInjectConstructorService.cs @@ -1,12 +1,11 @@ using Slim.Attributes; -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class DoNotInjectConstructorService { - public sealed class DoNotInjectConstructorService + [DoNotInject] + public DoNotInjectConstructorService() { - [DoNotInject] - public DoNotInjectConstructorService() - { - } } } diff --git a/Slim.Tests/Models/IDependentOnPrivateCtrService.cs b/Slim.Tests/Models/IDependentOnPrivateCtrService.cs index ff503b6..5fe9348 100644 --- a/Slim.Tests/Models/IDependentOnPrivateCtrService.cs +++ b/Slim.Tests/Models/IDependentOnPrivateCtrService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IDependentOnPrivateCtrService { - public interface IDependentOnPrivateCtrService - { - } } diff --git a/Slim.Tests/Models/IDependentOnServiceManagerService.cs b/Slim.Tests/Models/IDependentOnServiceManagerService.cs index c8fad13..ca8ce15 100644 --- a/Slim.Tests/Models/IDependentOnServiceManagerService.cs +++ b/Slim.Tests/Models/IDependentOnServiceManagerService.cs @@ -1,9 +1,8 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IDependentOnServiceManagerService { - public interface IDependentOnServiceManagerService - { - IServiceManager ServiceManager { get; } - IServiceProducer ServiceProducer { get; } - IServiceProvider ServiceProvider { get; } - } + IServiceManager ServiceManager { get; } + IServiceProducer ServiceProducer { get; } + IServiceProvider ServiceProvider { get; } } diff --git a/Slim.Tests/Models/IDependentOnThrowingService.cs b/Slim.Tests/Models/IDependentOnThrowingService.cs index c955b48..6eacd43 100644 --- a/Slim.Tests/Models/IDependentOnThrowingService.cs +++ b/Slim.Tests/Models/IDependentOnThrowingService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IDependentOnThrowingService { - public interface IDependentOnThrowingService - { - } } diff --git a/Slim.Tests/Models/IDependentService.cs b/Slim.Tests/Models/IDependentService.cs index d1d9646..70a084e 100644 --- a/Slim.Tests/Models/IDependentService.cs +++ b/Slim.Tests/Models/IDependentService.cs @@ -1,7 +1,6 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IDependentService { - public interface IDependentService - { - IIndependentService IndependentService { get; } - } + IIndependentService IndependentService { get; } } diff --git a/Slim.Tests/Models/IDependentService2.cs b/Slim.Tests/Models/IDependentService2.cs index 853082b..d8871f7 100644 --- a/Slim.Tests/Models/IDependentService2.cs +++ b/Slim.Tests/Models/IDependentService2.cs @@ -1,7 +1,6 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IDependentService2 { - public interface IDependentService2 - { - public IndependentService IndependentService { get; } - } + public IndependentService IndependentService { get; } } diff --git a/Slim.Tests/Models/IDisposableService.cs b/Slim.Tests/Models/IDisposableService.cs index 1fb6956..b5a2f27 100644 --- a/Slim.Tests/Models/IDisposableService.cs +++ b/Slim.Tests/Models/IDisposableService.cs @@ -1,12 +1,11 @@ -namespace Slim.Tests.Models -{ - public class IDisposableService : IIDisposableService - { - public bool DisposeCalled { get; set; } +namespace Slim.Tests.Models; - public void Dispose() - { - this.DisposeCalled = true; - } +public class IDisposableService : IIDisposableService +{ + public bool DisposeCalled { get; set; } + + public void Dispose() + { + this.DisposeCalled = true; } } diff --git a/Slim.Tests/Models/IDisposableSingletonService.cs b/Slim.Tests/Models/IDisposableSingletonService.cs index db68e2c..4e57171 100644 --- a/Slim.Tests/Models/IDisposableSingletonService.cs +++ b/Slim.Tests/Models/IDisposableSingletonService.cs @@ -1,12 +1,11 @@ -namespace Slim.Tests.Models -{ - public sealed class IDisposableSingletonService : IIDisposableService - { - public bool DisposeCalled { get; set; } +namespace Slim.Tests.Models; - public void Dispose() - { - this.DisposeCalled = true; - } +public sealed class IDisposableSingletonService : IIDisposableService +{ + public bool DisposeCalled { get; set; } + + public void Dispose() + { + this.DisposeCalled = true; } } diff --git a/Slim.Tests/Models/IIDisposableService.cs b/Slim.Tests/Models/IIDisposableService.cs index 0882921..002842e 100644 --- a/Slim.Tests/Models/IIDisposableService.cs +++ b/Slim.Tests/Models/IIDisposableService.cs @@ -1,9 +1,8 @@ using System; -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IIDisposableService : IDisposable { - public interface IIDisposableService : IDisposable - { - bool DisposeCalled { get; set; } - } + bool DisposeCalled { get; set; } } diff --git a/Slim.Tests/Models/IIndependentService.cs b/Slim.Tests/Models/IIndependentService.cs index f4dccb2..954849e 100644 --- a/Slim.Tests/Models/IIndependentService.cs +++ b/Slim.Tests/Models/IIndependentService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IIndependentService { - public interface IIndependentService - { - } } diff --git a/Slim.Tests/Models/IMultiInterfaceService1.cs b/Slim.Tests/Models/IMultiInterfaceService1.cs index 31d9848..39b2c0b 100644 --- a/Slim.Tests/Models/IMultiInterfaceService1.cs +++ b/Slim.Tests/Models/IMultiInterfaceService1.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IMultiInterfaceService1 { - public interface IMultiInterfaceService1 - { - } } diff --git a/Slim.Tests/Models/IMultiInterfaceService2.cs b/Slim.Tests/Models/IMultiInterfaceService2.cs index 93a542e..422f3fe 100644 --- a/Slim.Tests/Models/IMultiInterfaceService2.cs +++ b/Slim.Tests/Models/IMultiInterfaceService2.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IMultiInterfaceService2 { - public interface IMultiInterfaceService2 - { - } } diff --git a/Slim.Tests/Models/IPrivateConstructorService.cs b/Slim.Tests/Models/IPrivateConstructorService.cs index 91a1c56..856b2d3 100644 --- a/Slim.Tests/Models/IPrivateConstructorService.cs +++ b/Slim.Tests/Models/IPrivateConstructorService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IPrivateConstructorService { - public interface IPrivateConstructorService - { - } } diff --git a/Slim.Tests/Models/ISharedInterface.cs b/Slim.Tests/Models/ISharedInterface.cs index 9784ec4..e1ef3bf 100644 --- a/Slim.Tests/Models/ISharedInterface.cs +++ b/Slim.Tests/Models/ISharedInterface.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface ISharedInterface { - public interface ISharedInterface - { - } } diff --git a/Slim.Tests/Models/ISharedService1.cs b/Slim.Tests/Models/ISharedService1.cs index 3f6d485..f265d76 100644 --- a/Slim.Tests/Models/ISharedService1.cs +++ b/Slim.Tests/Models/ISharedService1.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface ISharedService1 : ISharedInterface { - public interface ISharedService1 : ISharedInterface - { - } } diff --git a/Slim.Tests/Models/ISharedService2.cs b/Slim.Tests/Models/ISharedService2.cs index 1d0b08c..a7d9551 100644 --- a/Slim.Tests/Models/ISharedService2.cs +++ b/Slim.Tests/Models/ISharedService2.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface ISharedService2 : ISharedInterface { - public interface ISharedService2 : ISharedInterface - { - } } diff --git a/Slim.Tests/Models/ISharedService3.cs b/Slim.Tests/Models/ISharedService3.cs index b07896a..05f3149 100644 --- a/Slim.Tests/Models/ISharedService3.cs +++ b/Slim.Tests/Models/ISharedService3.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface ISharedService3 : ISharedInterface { - public interface ISharedService3 : ISharedInterface - { - } } diff --git a/Slim.Tests/Models/IThrowingService.cs b/Slim.Tests/Models/IThrowingService.cs index 189ad8e..e155322 100644 --- a/Slim.Tests/Models/IThrowingService.cs +++ b/Slim.Tests/Models/IThrowingService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public interface IThrowingService { - public interface IThrowingService - { - } } diff --git a/Slim.Tests/Models/IndependentService.cs b/Slim.Tests/Models/IndependentService.cs index f90e2e6..5f9bb07 100644 --- a/Slim.Tests/Models/IndependentService.cs +++ b/Slim.Tests/Models/IndependentService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public class IndependentService : IIndependentService { - public class IndependentService : IIndependentService - { - } } diff --git a/Slim.Tests/Models/MultiConstructorService.cs b/Slim.Tests/Models/MultiConstructorService.cs index 90eb977..9ec64e3 100644 --- a/Slim.Tests/Models/MultiConstructorService.cs +++ b/Slim.Tests/Models/MultiConstructorService.cs @@ -1,13 +1,19 @@ -namespace Slim.Tests.Models -{ - public class MultiConstructorService : IIndependentService - { - public MultiConstructorService(int x, int y, object z) - { - } +namespace Slim.Tests.Models; - public MultiConstructorService() - { - } +public class MultiConstructorService : IIndependentService +{ + public int X { get; } + public int Y { get; } + public object Z { get; } + + public MultiConstructorService(int x, int y, object z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + public MultiConstructorService() + { } } diff --git a/Slim.Tests/Models/MultiInterfaceService.cs b/Slim.Tests/Models/MultiInterfaceService.cs index c027641..a5563cd 100644 --- a/Slim.Tests/Models/MultiInterfaceService.cs +++ b/Slim.Tests/Models/MultiInterfaceService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public class MultiInterfaceService : IMultiInterfaceService1, IMultiInterfaceService2 { - public class MultiInterfaceService : IMultiInterfaceService1, IMultiInterfaceService2 - { - } } diff --git a/Slim.Tests/Models/NoInterfaceService.cs b/Slim.Tests/Models/NoInterfaceService.cs index 4b5de33..06c67a7 100644 --- a/Slim.Tests/Models/NoInterfaceService.cs +++ b/Slim.Tests/Models/NoInterfaceService.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public class NoInterfaceService { - public class NoInterfaceService - { - } } diff --git a/Slim.Tests/Models/PrivateConstructorService.cs b/Slim.Tests/Models/PrivateConstructorService.cs index 4eb68b9..633e4f2 100644 --- a/Slim.Tests/Models/PrivateConstructorService.cs +++ b/Slim.Tests/Models/PrivateConstructorService.cs @@ -1,9 +1,8 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class PrivateConstructorService : IPrivateConstructorService { - public sealed class PrivateConstructorService : IPrivateConstructorService + private PrivateConstructorService() { - private PrivateConstructorService() - { - } } } diff --git a/Slim.Tests/Models/ServiceWithPrefferedConstructor.cs b/Slim.Tests/Models/ServiceWithPrefferedConstructor.cs index e9510e8..c258ce9 100644 --- a/Slim.Tests/Models/ServiceWithPrefferedConstructor.cs +++ b/Slim.Tests/Models/ServiceWithPrefferedConstructor.cs @@ -1,33 +1,38 @@ using Slim.Attributes; -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class ServiceWithPrefferedConstructor { - public sealed class ServiceWithPrefferedConstructor + public bool CalledPrefferedConstructor { get; init; } + public bool CalledPrefferedConstructor0 { get; init; } + public bool CalledPrefferedConstructor1 { get; init; } + public IIndependentService IndependentService { get; } + public IDependentService DependentService { get; } + public IDependentService2 DependentService2 { get; } + + public ServiceWithPrefferedConstructor() { - public bool CalledPrefferedConstructor { get; init; } - public bool CalledPrefferedConstructor0 { get; init; } - public bool CalledPrefferedConstructor1 { get; init; } + } - public ServiceWithPrefferedConstructor() - { - } + [PreferredConstructor] + public ServiceWithPrefferedConstructor(IIndependentService independentService) + { + this.CalledPrefferedConstructor = true; + this.IndependentService = independentService; + } - [PreferredConstructor] - public ServiceWithPrefferedConstructor(IIndependentService independentService) - { - this.CalledPrefferedConstructor = true; - } + [PreferredConstructor(Priority = 0)] + public ServiceWithPrefferedConstructor(IDependentService dependentService) + { + this.CalledPrefferedConstructor0 = true; + this.DependentService = dependentService; + } - [PreferredConstructor(Priority = 0)] - public ServiceWithPrefferedConstructor(IDependentService dependentService) - { - this.CalledPrefferedConstructor0 = true; - } - - [PreferredConstructor(Priority = 1)] - public ServiceWithPrefferedConstructor(IDependentService2 dependentService2) - { - this.CalledPrefferedConstructor1 = true; - } + [PreferredConstructor(Priority = 1)] + public ServiceWithPrefferedConstructor(IDependentService2 dependentService2) + { + this.CalledPrefferedConstructor1 = true; + this.DependentService2 = dependentService2; } } diff --git a/Slim.Tests/Models/SharedService1.cs b/Slim.Tests/Models/SharedService1.cs index d8f6587..6eab83f 100644 --- a/Slim.Tests/Models/SharedService1.cs +++ b/Slim.Tests/Models/SharedService1.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class SharedService1 : ISharedService1 { - public sealed class SharedService1 : ISharedService1 - { - } } diff --git a/Slim.Tests/Models/SharedService2.cs b/Slim.Tests/Models/SharedService2.cs index d33eaa5..90e2921 100644 --- a/Slim.Tests/Models/SharedService2.cs +++ b/Slim.Tests/Models/SharedService2.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class SharedService2 : ISharedService2 { - public sealed class SharedService2 : ISharedService2 - { - } } diff --git a/Slim.Tests/Models/SharedService3.cs b/Slim.Tests/Models/SharedService3.cs index 9f31dbe..8e9f981 100644 --- a/Slim.Tests/Models/SharedService3.cs +++ b/Slim.Tests/Models/SharedService3.cs @@ -1,6 +1,5 @@ -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class SharedService3 : ISharedService3 { - public sealed class SharedService3 : ISharedService3 - { - } } diff --git a/Slim.Tests/Models/ThrowingService.cs b/Slim.Tests/Models/ThrowingService.cs index 92c024f..560672b 100644 --- a/Slim.Tests/Models/ThrowingService.cs +++ b/Slim.Tests/Models/ThrowingService.cs @@ -1,12 +1,11 @@ using System; -namespace Slim.Tests.Models +namespace Slim.Tests.Models; + +public sealed class ThrowingService : IThrowingService { - public sealed class ThrowingService : IThrowingService + public ThrowingService() { - public ThrowingService() - { - throw new InvalidOperationException(); - } + throw new InvalidOperationException(); } } diff --git a/Slim.Tests/Resolvers/DependentServiceResolver.cs b/Slim.Tests/Resolvers/DependentServiceResolver.cs index 3206e74..693c3c5 100644 --- a/Slim.Tests/Resolvers/DependentServiceResolver.cs +++ b/Slim.Tests/Resolvers/DependentServiceResolver.cs @@ -2,24 +2,23 @@ using Slim.Tests.Models; using System; -namespace Slim.Tests.Resolvers +namespace Slim.Tests.Resolvers; + +public sealed class DependentServiceResolver : IDependencyResolver { - public sealed class DependentServiceResolver : IDependencyResolver + public bool CanResolve(Type type) { - public bool CanResolve(Type type) + if (type == typeof(IDependentService)) { - if (type == typeof(IDependentService)) - { - return true; - } - - return false; + return true; } - public object Resolve(IServiceProvider serviceProvider, Type type) - { - var independentService = serviceProvider.GetService(); - return new DependentService(independentService); - } + return false; + } + + public object Resolve(IServiceProvider serviceProvider, Type type) + { + var independentService = serviceProvider.GetService(); + return new DependentService(independentService); } } diff --git a/Slim.Tests/Resolvers/IndependentServiceResolver.cs b/Slim.Tests/Resolvers/IndependentServiceResolver.cs index c0e0f2f..4b80415 100644 --- a/Slim.Tests/Resolvers/IndependentServiceResolver.cs +++ b/Slim.Tests/Resolvers/IndependentServiceResolver.cs @@ -2,26 +2,25 @@ using Slim.Tests.Models; using System; -namespace Slim.Tests.Resolvers +namespace Slim.Tests.Resolvers; + +public class IndependentServiceResolver : IDependencyResolver { - public class IndependentServiceResolver : IDependencyResolver + public int Called { get; private set; } + + public bool CanResolve(Type type) { - public int Called { get; private set; } - - public bool CanResolve(Type type) + if (type == typeof(IIndependentService)) { - if (type == typeof(IIndependentService)) - { - return true; - } - - return false; + return true; } - public object Resolve(IServiceProvider serviceProvider, Type type) - { - this.Called++; - return new IndependentService(); - } + return false; + } + + public object Resolve(IServiceProvider serviceProvider, Type type) + { + this.Called++; + return new IndependentService(); } } diff --git a/Slim.Tests/ServiceManagerTests.cs b/Slim.Tests/ServiceManagerTests.cs index caab560..e0a6f3b 100644 --- a/Slim.Tests/ServiceManagerTests.cs +++ b/Slim.Tests/ServiceManagerTests.cs @@ -1,797 +1,814 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Slim.Exceptions; using Slim.Tests.Models; using Slim.Tests.Resolvers; using System; -namespace Slim.Tests +namespace Slim.Tests; + +[TestClass] +public class ServiceManagerTests { - [TestClass] - public class ServiceManagerTests + [TestMethod] + public void RegisterAndRetrieveServiceUsesExistingImplementationSingleton() { - [TestMethod] - public void RegisterAndRetrieveServiceUsesExistingImplementationSingleton() + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var independentService = di.GetService(); + independentService.Should().NotBeNull(); + + var dependentService = di.GetService(); + dependentService.Should().NotBeNull(); + + dependentService.IndependentService.Should().Be(independentService); + } + + [TestMethod] + public void DependencyOnPrivateConstructorServiceThrows() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); + di.GetService(); + }); - var independentService = di.GetService(); - independentService.Should().NotBeNull(); + action.Should().Throw(); + } - var dependentService = di.GetService(); - dependentService.Should().NotBeNull(); + [TestMethod] + public void DependencyOnThrowingServiceThrows() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); - dependentService.IndependentService.Should().Be(independentService); - } - - [TestMethod] - public void DependencyOnPrivateConstructorServiceThrows() + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); + di.GetService(); + }); - var action = new Action(() => - { - di.GetService(); - }); + action.Should().Throw(); + } - action.Should().Throw(); - } + [TestMethod] + public void ThrowingConstructorThrows() + { + var di = new ServiceManager(); + di.RegisterSingleton(); - [TestMethod] - public void DependencyOnThrowingServiceThrows() + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); + di.GetService(); + }); - var action = new Action(() => - { - di.GetService(); - }); + action.Should().Throw(); + } - action.Should().Throw(); - } + [TestMethod] + public void RetrieveThrowsNotRegistered() + { + var di = new ServiceManager(); + Assert.ThrowsException(() => { di.GetService(); }); + } - [TestMethod] - public void ThrowingConstructorThrows() + [TestMethod] + public void RegisterAndRetrieveThrowsNoSuitableConstructorFound() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + Assert.ThrowsException(() => { di.GetService(); }); + } + + [TestMethod] + public void RegisterMultipleServices() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + } + + [TestMethod] + public void RegisterAndRetrieveService() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var dependentService = di.GetService(); + dependentService.Should().NotBeNull(); + } + + [TestMethod] + public void TransientAreDifferent() + { + var di = new ServiceManager(); + di.RegisterTransient(); + + var independentService = di.GetService(); + var independentService2 = di.GetService(); + + independentService.Should().NotBeSameAs(independentService2); + } + + [TestMethod] + public void SingletonAreTheSame() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var independentService = di.GetService(); + var independentService2 = di.GetService(); + + independentService.Should().BeSameAs(independentService2); + } + + [TestMethod] + public void RegisterServiceWithFactoryAndRetrieve() + { + var calledFactory = false; + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(sp => { - var di = new ServiceManager(); - di.RegisterSingleton(); + calledFactory = true; + return new DependentService(sp.GetService()); + }); - var action = new Action(() => - { - di.GetService(); - }); + var dependentService = di.GetService(); - action.Should().Throw(); - } + dependentService.Should().NotBeNull(); + calledFactory.Should().BeTrue(); + } - [TestMethod] - public void RetrieveThrowsNotRegistered() + [TestMethod] + public void TransientDependentAndSingletonIndependentRespectLifetime() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterTransient(); + + var independentService = di.GetService(); + var dependentService = di.GetService(); + var dependentService2 = di.GetService(); + + dependentService.Should().NotBeSameAs(dependentService2); + dependentService.IndependentService.Should().BeSameAs(dependentService2.IndependentService); + independentService.Should().BeSameAs(dependentService.IndependentService); + } + + [TestMethod] + public void TransientDependentWithFactoryAndSingletonIndependentRespectLifetime() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterTransient(sp => new DependentService(sp.GetService())); + + var independentService = di.GetService(); + var dependentService = di.GetService(); + var dependentService2 = di.GetService(); + + dependentService.Should().NotBeSameAs(dependentService2); + dependentService.IndependentService.Should().BeSameAs(dependentService2.IndependentService); + independentService.Should().BeSameAs(dependentService.IndependentService); + } + + [TestMethod] + public void MultipleDeclarationsReturnSameSingleton() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var independentService = di.GetService(); + var sameIndependentService = di.GetService(); + + independentService.Should().BeSameAs(sameIndependentService); + } + + [TestMethod] + public void MultipleDeclarationsOfSameInterfaceThrows() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var action = new Action(() => { - var di = new ServiceManager(); - Assert.ThrowsException(() => { di.GetService(); }); - } - - [TestMethod] - public void RegisterAndRetrieveThrowsNoSuitableConstructorFound() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - Assert.ThrowsException(() => { di.GetService(); }); - } - - [TestMethod] - public void RegisterMultipleServices() - { - var di = new ServiceManager(); di.RegisterSingleton(); - di.RegisterSingleton(); - } + }); - [TestMethod] - public void RegisterAndRetrieveService() + action.Should().Throw(); + } + + [TestMethod] + public void MultiInterfaceDeclarationReturnsSameSingleton() + { + var di = new ServiceManager(); + di.RegisterSingleton(registerAllInterfaces: true); + + var singleton1 = di.GetService(); + var singleton2 = di.GetService(); + + singleton1.Should().Be(singleton2); + } + + [TestMethod] + public void MultiInterfaceDeclarationReturnsDifferentTransient() + { + var di = new ServiceManager(); + di.RegisterTransient(registerAllInterfaces: true); + + var transient1 = di.GetService(); + var transient2 = di.GetService(); + + transient1.Should().NotBe(transient2); + } + + [TestMethod] + public void MultiInterfaceDeclarationWithFactoryReturnsSameSingleton() + { + var di = new ServiceManager(); + di.RegisterSingleton(sp => new MultiInterfaceService(), registerAllInterfaces: true); + + var singleton1 = di.GetService(); + var singleton2 = di.GetService(); + + singleton1.Should().Be(singleton2); + } + + [TestMethod] + public void MultiInterfaceDeclarationWithFactoryReturnsDifferentTransient() + { + var di = new ServiceManager(); + di.RegisterTransient(sp => new MultiInterfaceService(), registerAllInterfaces: true); + + var transient1 = di.GetService(); + var transient2 = di.GetService(); + + transient1.Should().NotBe(transient2); + } + + [TestMethod] + public void RegisterServiceManagerSingletonRegistersCurrentServiceManager() + { + var di = new ServiceManager(); + di.RegisterServiceManager(); + di.RegisterSingleton(); + + var dependentService = di.GetService(); + + dependentService.ServiceManager.Should().Be(di); + dependentService.ServiceProducer.Should().Be(di); + dependentService.ServiceProvider.Should().Be(di); + } + + [TestMethod] + public void CatchSpecificException() + { + var di = new ServiceManager(); + var thrown = false; + di.HandleException((sp, e) => + { + thrown = true; + return true; + }); + + di.RegisterSingleton(); + di.RegisterSingleton(); + + thrown.Should().BeTrue(); + } + + [TestMethod] + public void CatchSpecificExceptionAndRethrow() + { + var di = new ServiceManager(); + var thrown = false; + di.HandleException((sp, e) => + { + thrown = true; + return false; + }); + + di.RegisterSingleton(); + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); di.RegisterSingleton(); + }); - var dependentService = di.GetService(); - dependentService.Should().NotBeNull(); - } + action.Should().Throw(); + thrown.Should().BeTrue(); + } - [TestMethod] - public void TransientAreDifferent() + [TestMethod] + public void ClearAndGetShouldThrow() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterTransient(); + di.GetService(); + }); + di.Clear(); - var independentService = di.GetService(); - var independentService2 = di.GetService(); + action.Should().Throw(); + } - independentService.Should().NotBeSameAs(independentService2); - } + [TestMethod] + public void ClearShouldCallDispose() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + var disposableService = (IDisposableService)di.GetService(); - [TestMethod] - public void SingletonAreTheSame() + di.Clear(); + + disposableService.DisposeCalled.Should().BeTrue(); + } + + [TestMethod] + public void BuildAllSingletons() + { + var shouldCallConstructor = false; + var shouldNotCallConstructor = false; + var di = new ServiceManager(); + di.RegisterSingleton(sp => { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var independentService = di.GetService(); - var independentService2 = di.GetService(); - - independentService.Should().BeSameAs(independentService2); - } - - [TestMethod] - public void RegisterServiceWithFactoryAndRetrieve() + shouldCallConstructor = true; + return new IndependentService(); + }); + di.RegisterTransient(sp => { - var calledFactory = false; - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(sp => - { - calledFactory = true; - return new DependentService(sp.GetService()); - }); + shouldNotCallConstructor = true; + return new DependentService(sp.GetService()); + }); - var dependentService = di.GetService(); + di.BuildSingletons(); - dependentService.Should().NotBeNull(); - calledFactory.Should().BeTrue(); - } + shouldCallConstructor.Should().BeTrue(); + shouldNotCallConstructor.Should().BeFalse(); + } - [TestMethod] - public void TransientDependentAndSingletonIndependentRespectLifetime() + [TestMethod] + public void MultipleConstructorsShouldFindUsableOne() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + } + + [TestMethod] + public void NoInterfaceServiceShouldReturnService() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + } + + [TestMethod] + public void UseResolverToResolveService() + { + var di = new ServiceManager(); + var resolver = new IndependentServiceResolver(); + di.RegisterResolver(resolver); + di.RegisterTransient(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + resolver.Called.Should().Be(1); + } + + [TestMethod] + public void UseResolverWithoutRegisteringServiceCallsResolver() + { + var di = new ServiceManager(); + var resolver = new IndependentServiceResolver(); + di.RegisterResolver(resolver); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + } + + [TestMethod] + public void UseResolverCalledOnceForSingleton() + { + var di = new ServiceManager(); + var resolver = new IndependentServiceResolver(); + di.RegisterResolver(resolver); + di.RegisterSingleton(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + resolver.Called.Should().Be(1); + + var service2 = di.GetService(); + service2.Should().NotBeNull(); + service2.Should().BeOfType(); + resolver.Called.Should().Be(1); + + service.Should().Be(service2); + } + + [TestMethod] + public void UseResolverCalledMultipleForTransient() + { + var di = new ServiceManager(); + var resolver = new IndependentServiceResolver(); + di.RegisterResolver(resolver); + di.RegisterTransient(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + resolver.Called.Should().Be(1); + + var service2 = di.GetService(); + service2.Should().NotBeNull(); + service2.Should().BeOfType(); + resolver.Called.Should().Be(2); + } + + [TestMethod] + public void UseResolverForUndeclaredService() + { + var di = new ServiceManager(); + var resolver = new IndependentServiceResolver(); + di.RegisterResolver(resolver); + di.RegisterTransient(); + + var service = di.GetService(); + service.Should().NotBeNull(); + resolver.Called.Should().Be(1); + } + + [TestMethod] + public void UseResolverUsesSecondResolver() + { + var di = new ServiceManager(); + di.RegisterResolver(new IndependentServiceResolver()); + di.RegisterResolver(new DependentServiceResolver()); + di.RegisterTransient(); + di.RegisterTransient(); + + var service = di.GetService(); + service.Should().NotBeNull(); + service.Should().BeOfType(); + + var service2 = di.GetService(); + service2.Should().NotBeNull(); + service2.Should().BeOfType(); + } + + [TestMethod] + public void CreateScopeCreatesNewServiceProvider() + { + var di = new ServiceManager(); + var scopedDi = di.CreateScope(); + scopedDi.Should().NotBeNull(); + scopedDi.Should().NotBe(di); + } + + [TestMethod] + public void ScopedServicesShouldBeNewUnderScopedProvider() + { + var di = new ServiceManager(); + di.RegisterScoped(); + + var service = di.GetService(); + var scopedDi = di.CreateScope(); + var scopedService = scopedDi.GetService(); + + service.Should().NotBe(scopedService); + } + + [TestMethod] + public void SingletonServicesShouldBeSameUnderScopedProvider() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + var scopedDi = di.CreateScope(); + + var service = di.GetService(); + var scopedService = scopedDi.GetService(); + + service.Should().Be(scopedService); + } + + [TestMethod] + public void LifetimesShouldBeRespected() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterScoped(); + di.BuildSingletons(); + var scopedDi = di.CreateScope(); + + var independentService1 = di.GetService(); + var dependentService1 = di.GetService(); + var independentService2 = scopedDi.GetService(); + var dependentService2 = scopedDi.GetService(); + + independentService1.Should().Be(independentService2); + dependentService1.Should().NotBe(dependentService2); + independentService1.Should().NotBeNull(); + independentService2.Should().NotBeNull(); + dependentService1.Should().NotBeNull(); + dependentService2.Should().NotBeNull(); + } + + [TestMethod] + public void ScopedManagerShouldHaveItsOwnLists() + { + var di = new ServiceManager() { AllowScopedManagerModifications = true }; + di.RegisterSingleton(); + var scopedDi = di.CreateScope(); + + di.RegisterSingleton(); + scopedDi.As().RegisterSingleton(); + + var dependentService1 = di.GetService(); + var dependentService2 = scopedDi.GetService(); + + dependentService1.Should().NotBe(dependentService2); + } + + [TestMethod] + public void ScopedManagerCallsOriginalFactoryOnlyOnce() + { + var di = new ServiceManager(); + var called = 0; + di.RegisterSingleton(sp => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterTransient(); + called++; + return new IndependentService(); + }); + var scopedDi = di.CreateScope(); - var independentService = di.GetService(); - var dependentService = di.GetService(); - var dependentService2 = di.GetService(); + scopedDi.GetService(); + called.Should().Be(1); + scopedDi.GetService(); + called.Should().Be(1); + di.GetService(); + called.Should().Be(1); + } - dependentService.Should().NotBeSameAs(dependentService2); - dependentService.IndependentService.Should().BeSameAs(dependentService2.IndependentService); - independentService.Should().BeSameAs(dependentService.IndependentService); - } + [TestMethod] + public void GetServicesReturnsAllServicesWithInterface() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); - [TestMethod] - public void TransientDependentWithFactoryAndSingletonIndependentRespectLifetime() + di.GetServicesOfType().Should().HaveCount(3); + di.GetServicesOfType(typeof(ISharedInterface)).Should().HaveCount(3).And.AllBeAssignableTo(); + } + + [TestMethod] + public void DisposingServiceManagerShouldDisposeInstances() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var disposableService = di.GetService(); + di.Dispose(); + + disposableService.DisposeCalled.Should().BeTrue(); + } + + [TestMethod] + public void DisposingServiceManagerShouldDisposeAllServices() + { + var di = new ServiceManager(); + di.RegisterScoped(); + di.RegisterSingleton(); + + var disposableService = di.GetService(); + var disposableSingletonService = di.GetService(); + di.Dispose(); + + disposableSingletonService.DisposeCalled.Should().BeTrue(); + disposableService.DisposeCalled.Should().BeTrue(); + } + + [TestMethod] + public void DisposingScopedServiceManagerShouldDisposeOnlyScopedServices() + { + var di = new ServiceManager(); + di.RegisterScoped(); + di.RegisterSingleton(); + + var disposableSingletonService = di.GetService(); + var scopedDi = di.CreateScope(); + var disposableService = scopedDi.GetService(); + scopedDi.Dispose(); + + disposableSingletonService.DisposeCalled.Should().BeFalse(); + disposableService.DisposeCalled.Should().BeTrue(); + } + + [TestMethod] + public void RegisterAllInterfacesFalseShouldRegisterOnlyTypeOfObject() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + di.GetService().Should().NotBeNull(); + var action = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterTransient(sp => new DependentService(sp.GetService())); - - var independentService = di.GetService(); - var dependentService = di.GetService(); - var dependentService2 = di.GetService(); - - dependentService.Should().NotBeSameAs(dependentService2); - dependentService.IndependentService.Should().BeSameAs(dependentService2.IndependentService); - independentService.Should().BeSameAs(dependentService.IndependentService); - } - - [TestMethod] - public void MultipleDeclarationsReturnSameSingleton() + di.GetService(); + }); + var action2 = new Action(() => { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); + di.GetService(); + }); - var independentService = di.GetService(); - var sameIndependentService = di.GetService(); + action.Should().Throw(); + action2.Should().Throw(); + } - independentService.Should().BeSameAs(sameIndependentService); - } + [TestMethod] + public void IsRegisteredReturnsTrueForRegisteredService() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(typeof(IDependentService), typeof(DependentService)); - [TestMethod] - public void MultipleDeclarationsOfSameInterfaceThrows() + var isRegistered1 = di.IsRegistered(); + var isRegistered2 = di.IsRegistered(typeof(IDependentService)); + + isRegistered1.Should().BeTrue(); + isRegistered2.Should().BeTrue(); + } + + [TestMethod] + public void IsRegisteredReturnsFalseForNonRegisteredService() + { + var di = new ServiceManager(); + + var isRegistered1 = di.IsRegistered(); + var isRegistered2 = di.IsRegistered(typeof(IDependentService)); + + isRegistered1.Should().BeFalse(); + isRegistered2.Should().BeFalse(); + } + + [TestMethod] + public void IsRegisteredReturnsTrueForDependencyResolver() + { + var di = new ServiceManager(); + di.RegisterResolver(new DependentServiceResolver()); + + var isRegistered = di.IsRegistered(); + + isRegistered.Should().BeTrue(); + } + + [TestMethod] + public void ScopedServiceManagerWithDisallowedModificationsIsReadOnly() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + var scopedDi1 = di.CreateScope() as ServiceManager; + + scopedDi1.IsReadOnly.Should().BeTrue(); + } + + [TestMethod] + public void ScopedServiceManagerWithAllowedModificationsIsNotReadOnly() + { + var di = new ServiceManager() { AllowScopedManagerModifications = true }; + di.RegisterSingleton(); + var scopedDi1 = di.CreateScope() as ServiceManager; + + scopedDi1.IsReadOnly.Should().BeFalse(); + } + + [TestMethod] + public void ModifyingScopedManagerThrowsInvalidOperationException() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + var scopedDi1 = di.CreateScope() as ServiceManager; + + var action = new Action(() => scopedDi1.RegisterSingleton()); + + action.Should().Throw(); + } + + [TestMethod] + public void ModifyingScopedManagerStillResolvesSingleton() + { + var di = new ServiceManager() { AllowScopedManagerModifications = true }; + di.RegisterSingleton(); + var scopedDi1 = di.CreateScope() as ServiceManager; + var scopedDi2 = di.CreateScope() as ServiceManager; + scopedDi1.RegisterSingleton(); + + var dependentService1 = scopedDi1.GetService(); + var dependentService2 = scopedDi2.GetService(); + + dependentService1.Should().Be(dependentService2); + dependentService1.IndependentService.Should().Be(dependentService2.IndependentService); + } + + [TestMethod] + public void ServiceWithPrefferedConstructorCallsPrefferedConstructor() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var service = di.GetService(); + + service.CalledPrefferedConstructor.Should().BeTrue(); + } + + [TestMethod] + public void ServiceWithPrefferedConstructorFallsBackToOtherConstructors() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var service = di.GetService(); + + service.CalledPrefferedConstructor.Should().BeFalse(); + } + + [TestMethod] + public void ServiceWithPrefferedConstructorCallsPrefferedConstructorWithHighestPriority() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var service = di.GetService(); + + service.CalledPrefferedConstructor0.Should().BeTrue(); + } + + [TestMethod] + public void ServiceWithPrefferedConstructorCallsPrefferedConstructorWithSecondHighestPriority() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + di.RegisterSingleton(); + + var service = di.GetService(); + + service.CalledPrefferedConstructor1.Should().BeTrue(); + } + + [TestMethod] + public void ServiceWithDoNotInjectAttribute_FailsToInject() + { + var di = new ServiceManager(); + di.RegisterSingleton(); + + var action = () => { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var action = new Action(() => - { - di.RegisterSingleton(); - }); - - action.Should().Throw(); - } - - [TestMethod] - public void MultiInterfaceDeclarationReturnsSameSingleton() - { - var di = new ServiceManager(); - di.RegisterSingleton(registerAllInterfaces: true); - - var singleton1 = di.GetService(); - var singleton2 = di.GetService(); - - singleton1.Should().Be(singleton2); - } - - [TestMethod] - public void MultiInterfaceDeclarationReturnsDifferentTransient() - { - var di = new ServiceManager(); - di.RegisterTransient(registerAllInterfaces: true); - - var transient1 = di.GetService(); - var transient2 = di.GetService(); - - transient1.Should().NotBe(transient2); - } - - [TestMethod] - public void MultiInterfaceDeclarationWithFactoryReturnsSameSingleton() - { - var di = new ServiceManager(); - di.RegisterSingleton(sp => new MultiInterfaceService(), registerAllInterfaces: true); - - var singleton1 = di.GetService(); - var singleton2 = di.GetService(); - - singleton1.Should().Be(singleton2); - } - - [TestMethod] - public void MultiInterfaceDeclarationWithFactoryReturnsDifferentTransient() - { - var di = new ServiceManager(); - di.RegisterTransient(sp => new MultiInterfaceService(), registerAllInterfaces: true); - - var transient1 = di.GetService(); - var transient2 = di.GetService(); - - transient1.Should().NotBe(transient2); - } - - [TestMethod] - public void RegisterServiceManagerSingletonRegistersCurrentServiceManager() - { - var di = new ServiceManager(); - di.RegisterServiceManager(); - di.RegisterSingleton(); - - var dependentService = di.GetService(); - - dependentService.ServiceManager.Should().Be(di); - dependentService.ServiceProducer.Should().Be(di); - dependentService.ServiceProvider.Should().Be(di); - } - - [TestMethod] - public void CatchSpecificException() - { - var di = new ServiceManager(); - var thrown = false; - di.HandleException((sp, e) => - { - thrown = true; - return true; - }); - - di.RegisterSingleton(); - di.RegisterSingleton(); - - thrown.Should().BeTrue(); - } - - [TestMethod] - public void CatchSpecificExceptionAndRethrow() - { - var di = new ServiceManager(); - var thrown = false; - di.HandleException((sp, e) => - { - thrown = true; - return false; - }); - - di.RegisterSingleton(); - var action = new Action(() => - { - di.RegisterSingleton(); - }); - - action.Should().Throw(); - thrown.Should().BeTrue(); - } - - [TestMethod] - public void ClearAndGetShouldThrow() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var action = new Action(() => - { - di.GetService(); - }); - di.Clear(); - - action.Should().Throw(); - } - - [TestMethod] - public void ClearShouldCallDispose() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - var disposableService = (IDisposableService)di.GetService(); - - di.Clear(); - - disposableService.DisposeCalled.Should().BeTrue(); - } - - [TestMethod] - public void BuildAllSingletons() - { - var shouldCallConstructor = false; - var shouldNotCallConstructor = false; - var di = new ServiceManager(); - di.RegisterSingleton(sp => - { - shouldCallConstructor = true; - return new IndependentService(); - }); - di.RegisterTransient(sp => - { - shouldNotCallConstructor = true; - return new DependentService(sp.GetService()); - }); - - di.BuildSingletons(); - - shouldCallConstructor.Should().BeTrue(); - shouldNotCallConstructor.Should().BeFalse(); - } - - [TestMethod] - public void MultipleConstructorsShouldFindUsableOne() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - } - - [TestMethod] - public void NoInterfaceServiceShouldReturnService() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - } - - [TestMethod] - public void UseResolverToResolveService() - { - var di = new ServiceManager(); - var resolver = new IndependentServiceResolver(); - di.RegisterResolver(resolver); - di.RegisterTransient(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - resolver.Called.Should().Be(1); - } - - [TestMethod] - public void UseResolverWithoutRegisteringServiceCallsResolver() - { - var di = new ServiceManager(); - var resolver = new IndependentServiceResolver(); - di.RegisterResolver(resolver); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - } - - [TestMethod] - public void UseResolverCalledOnceForSingleton() - { - var di = new ServiceManager(); - var resolver = new IndependentServiceResolver(); - di.RegisterResolver(resolver); - di.RegisterSingleton(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - resolver.Called.Should().Be(1); - - var service2 = di.GetService(); - service2.Should().NotBeNull(); - service2.Should().BeOfType(); - resolver.Called.Should().Be(1); - - service.Should().Be(service2); - } - - [TestMethod] - public void UseResolverCalledMultipleForTransient() - { - var di = new ServiceManager(); - var resolver = new IndependentServiceResolver(); - di.RegisterResolver(resolver); - di.RegisterTransient(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - resolver.Called.Should().Be(1); - - var service2 = di.GetService(); - service2.Should().NotBeNull(); - service2.Should().BeOfType(); - resolver.Called.Should().Be(2); - } - - [TestMethod] - public void UseResolverForUndeclaredService() - { - var di = new ServiceManager(); - var resolver = new IndependentServiceResolver(); - di.RegisterResolver(resolver); - di.RegisterTransient(); - - var service = di.GetService(); - service.Should().NotBeNull(); - resolver.Called.Should().Be(1); - } - - [TestMethod] - public void UseResolverUsesSecondResolver() - { - var di = new ServiceManager(); - di.RegisterResolver(new IndependentServiceResolver()); - di.RegisterResolver(new DependentServiceResolver()); - di.RegisterTransient(); - di.RegisterTransient(); - - var service = di.GetService(); - service.Should().NotBeNull(); - service.Should().BeOfType(); - - var service2 = di.GetService(); - service2.Should().NotBeNull(); - service2.Should().BeOfType(); - } - - [TestMethod] - public void CreateScopeCreatesNewServiceProvider() - { - var di = new ServiceManager(); - var scopedDi = di.CreateScope(); - scopedDi.Should().NotBeNull(); - scopedDi.Should().NotBe(di); - } - - [TestMethod] - public void ScopedServicesShouldBeNewUnderScopedProvider() - { - var di = new ServiceManager(); - di.RegisterScoped(); - - var service = di.GetService(); - var scopedDi = di.CreateScope(); - var scopedService = scopedDi.GetService(); - - service.Should().NotBe(scopedService); - } - - [TestMethod] - public void SingletonServicesShouldBeSameUnderScopedProvider() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - var scopedDi = di.CreateScope(); - - var service = di.GetService(); - var scopedService = scopedDi.GetService(); - - service.Should().Be(scopedService); - } - - [TestMethod] - public void LifetimesShouldBeRespected() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterScoped(); - di.BuildSingletons(); - var scopedDi = di.CreateScope(); - - var independentService1 = di.GetService(); - var dependentService1 = di.GetService(); - var independentService2 = scopedDi.GetService(); - var dependentService2 = scopedDi.GetService(); - - independentService1.Should().Be(independentService2); - dependentService1.Should().NotBe(dependentService2); - independentService1.Should().NotBeNull(); - independentService2.Should().NotBeNull(); - dependentService1.Should().NotBeNull(); - dependentService2.Should().NotBeNull(); - } - - [TestMethod] - public void ScopedManagerShouldHaveItsOwnLists() - { - var di = new ServiceManager() { AllowScopedManagerModifications = true }; - di.RegisterSingleton(); - var scopedDi = di.CreateScope(); - - di.RegisterSingleton(); - scopedDi.As().RegisterSingleton(); - - var dependentService1 = di.GetService(); - var dependentService2 = scopedDi.GetService(); - - dependentService1.Should().NotBe(dependentService2); - } - - [TestMethod] - public void ScopedManagerCallsOriginalFactoryOnlyOnce() - { - var di = new ServiceManager(); - var called = 0; - di.RegisterSingleton(sp => - { - called++; - return new IndependentService(); - }); - var scopedDi = di.CreateScope(); - - scopedDi.GetService(); - called.Should().Be(1); - scopedDi.GetService(); - called.Should().Be(1); - di.GetService(); - called.Should().Be(1); - } - - [TestMethod] - public void GetServicesReturnsAllServicesWithInterface() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - - di.GetServicesOfType().Should().HaveCount(3); - di.GetServicesOfType(typeof(ISharedInterface)).Should().HaveCount(3).And.AllBeAssignableTo(); - } - - [TestMethod] - public void DisposingServiceManagerShouldDisposeInstances() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var disposableService = di.GetService(); - di.Dispose(); - - disposableService.DisposeCalled.Should().BeTrue(); - } - - [TestMethod] - public void DisposingServiceManagerShouldDisposeAllServices() - { - var di = new ServiceManager(); - di.RegisterScoped(); - di.RegisterSingleton(); - - var disposableService = di.GetService(); - var disposableSingletonService = di.GetService(); - di.Dispose(); - - disposableSingletonService.DisposeCalled.Should().BeTrue(); - disposableService.DisposeCalled.Should().BeTrue(); - } - - [TestMethod] - public void DisposingScopedServiceManagerShouldDisposeOnlyScopedServices() - { - var di = new ServiceManager(); - di.RegisterScoped(); - di.RegisterSingleton(); - - var disposableSingletonService = di.GetService(); - var scopedDi = di.CreateScope(); - var disposableService = scopedDi.GetService(); - scopedDi.Dispose(); - - disposableSingletonService.DisposeCalled.Should().BeFalse(); - disposableService.DisposeCalled.Should().BeTrue(); - } - - [TestMethod] - public void RegisterAllInterfacesFalseShouldRegisterOnlyTypeOfObject() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - di.GetService().Should().NotBeNull(); - var action = new Action(() => - { - di.GetService(); - }); - var action2 = new Action(() => - { - di.GetService(); - }); - - action.Should().Throw(); - action2.Should().Throw(); - } - - [TestMethod] - public void IsRegisteredReturnsTrueForRegisteredService() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(typeof(IDependentService), typeof(DependentService)); - - var isRegistered1 = di.IsRegistered(); - var isRegistered2 = di.IsRegistered(typeof(IDependentService)); - - isRegistered1.Should().BeTrue(); - isRegistered2.Should().BeTrue(); - } - - [TestMethod] - public void IsRegisteredReturnsFalseForNonRegisteredService() - { - var di = new ServiceManager(); - - var isRegistered1 = di.IsRegistered(); - var isRegistered2 = di.IsRegistered(typeof(IDependentService)); - - isRegistered1.Should().BeFalse(); - isRegistered2.Should().BeFalse(); - } - - [TestMethod] - public void IsRegisteredReturnsTrueForDependencyResolver() - { - var di = new ServiceManager(); - di.RegisterResolver(new DependentServiceResolver()); - - var isRegistered = di.IsRegistered(); - - isRegistered.Should().BeTrue(); - } - - [TestMethod] - public void ScopedServiceManagerWithDisallowedModificationsIsReadOnly() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - var scopedDi1 = di.CreateScope() as ServiceManager; - - scopedDi1.IsReadOnly.Should().BeTrue(); - } - - [TestMethod] - public void ScopedServiceManagerWithAllowedModificationsIsNotReadOnly() - { - var di = new ServiceManager() { AllowScopedManagerModifications = true }; - di.RegisterSingleton(); - var scopedDi1 = di.CreateScope() as ServiceManager; - - scopedDi1.IsReadOnly.Should().BeFalse(); - } - - [TestMethod] - public void ModifyingScopedManagerThrowsInvalidOperationException() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - var scopedDi1 = di.CreateScope() as ServiceManager; - - var action = new Action(() => scopedDi1.RegisterSingleton()); - - action.Should().Throw(); - } - - [TestMethod] - public void ModifyingScopedManagerStillResolvesSingleton() - { - var di = new ServiceManager() { AllowScopedManagerModifications = true }; - di.RegisterSingleton(); - var scopedDi1 = di.CreateScope() as ServiceManager; - var scopedDi2 = di.CreateScope() as ServiceManager; - scopedDi1.RegisterSingleton(); - - var dependentService1 = scopedDi1.GetService(); - var dependentService2 = scopedDi2.GetService(); - - dependentService1.Should().Be(dependentService2); - dependentService1.IndependentService.Should().Be(dependentService2.IndependentService); - } - - [TestMethod] - public void ServiceWithPrefferedConstructorCallsPrefferedConstructor() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); - var service = di.GetService(); + }; - service.CalledPrefferedConstructor.Should().BeTrue(); - } + action.Should().Throw(); + } - [TestMethod] - public void ServiceWithPrefferedConstructorFallsBackToOtherConstructors() - { - var di = new ServiceManager(); - di.RegisterSingleton(); + [TestMethod] + public void ServiceManager_SetsParentOfScopedManager() + { + var serviceManager = new ServiceManager(); - var service = di.GetService(); + var scopedManager = serviceManager.CreateScope() as IServiceManager; - service.CalledPrefferedConstructor.Should().BeFalse(); - } + scopedManager.ParentServiceManager.Should().Be(serviceManager); + } - [TestMethod] - public void ServiceWithPrefferedConstructorCallsPrefferedConstructorWithHighestPriority() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); + [TestMethod] + public void ServiceManager_ShouldHaveNoParent() + { + var serviceManager = new ServiceManager(); - var service = di.GetService(); - - service.CalledPrefferedConstructor0.Should().BeTrue(); - } - - [TestMethod] - public void ServiceWithPrefferedConstructorCallsPrefferedConstructorWithSecondHighestPriority() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - di.RegisterSingleton(); - - var service = di.GetService(); - - service.CalledPrefferedConstructor1.Should().BeTrue(); - } - - [TestMethod] - public void ServiceWithDoNotInjectAttribute_FailsToInject() - { - var di = new ServiceManager(); - di.RegisterSingleton(); - - var action = () => - { - var service = di.GetService(); - }; - - action.Should().Throw(); - } + serviceManager.ParentServiceManager.Should().BeNull(); } } diff --git a/Slim.sln b/Slim.sln index fdcb3b6..5f272ba 100644 --- a/Slim.sln +++ b/Slim.sln @@ -16,6 +16,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pipelines", "Pipelines", "{ .github\workflows\ci.yaml = .github\workflows\ci.yaml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slim.Integration", "Slim.Integration\Slim.Integration.csproj", "{13975492-5299-4589-91C1-C33E4FCEDD72}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D20E2AFD-6BE0-4CCC-AD87-D2961CD1763B}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +37,10 @@ Global {F03F7353-0BD5-4691-A318-BC6FC93AB00E}.Debug|Any CPU.Build.0 = Debug|Any CPU {F03F7353-0BD5-4691-A318-BC6FC93AB00E}.Release|Any CPU.ActiveCfg = Release|Any CPU {F03F7353-0BD5-4691-A318-BC6FC93AB00E}.Release|Any CPU.Build.0 = Release|Any CPU + {13975492-5299-4589-91C1-C33E4FCEDD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13975492-5299-4589-91C1-C33E4FCEDD72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13975492-5299-4589-91C1-C33E4FCEDD72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13975492-5299-4589-91C1-C33E4FCEDD72}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Slim/Attributes/DoNotInjectAttribute.cs b/Slim/Attributes/DoNotInjectAttribute.cs index 900f96c..6d852c5 100644 --- a/Slim/Attributes/DoNotInjectAttribute.cs +++ b/Slim/Attributes/DoNotInjectAttribute.cs @@ -1,12 +1,11 @@ using System; -namespace Slim.Attributes +namespace Slim.Attributes; + +/// +/// Attribute used to mark constructors to not be used by the . +/// +[AttributeUsage(AttributeTargets.Constructor)] +public sealed class DoNotInjectAttribute : Attribute { - /// - /// Attribute used to mark constructors to not be used by the . - /// - [AttributeUsage(AttributeTargets.Constructor)] - public sealed class DoNotInjectAttribute : Attribute - { - } } diff --git a/Slim/Attributes/PrefferedConstructorAttribute.cs b/Slim/Attributes/PrefferedConstructorAttribute.cs index 5a31a07..a4779cb 100644 --- a/Slim/Attributes/PrefferedConstructorAttribute.cs +++ b/Slim/Attributes/PrefferedConstructorAttribute.cs @@ -1,23 +1,22 @@ using System; -namespace Slim.Attributes +namespace Slim.Attributes; + +/// +/// Attribute used to mark constructors to be used with priority by the . +/// +[AttributeUsage(AttributeTargets.Constructor)] +public sealed class PreferredConstructorAttribute : Attribute { /// - /// Attribute used to mark constructors to be used with priority by the . + /// Priority of the constructor. Constructors are ordered using this value when present. /// - [AttributeUsage(AttributeTargets.Constructor)] - public sealed class PreferredConstructorAttribute : Attribute - { - /// - /// Priority of the constructor. Constructors are ordered using this value when present. - /// - public int Priority { get; set; } = int.MaxValue - 1; + public int Priority { get; set; } = int.MaxValue - 1; - /// - /// Creates a new instance of . - /// - public PreferredConstructorAttribute() - { - } + /// + /// Creates a new instance of . + /// + public PreferredConstructorAttribute() + { } } diff --git a/Slim/Exceptions/DependencyInjectionException.cs b/Slim/Exceptions/DependencyInjectionException.cs index d00013d..798d59e 100644 --- a/Slim/Exceptions/DependencyInjectionException.cs +++ b/Slim/Exceptions/DependencyInjectionException.cs @@ -1,44 +1,43 @@ using System; using System.Runtime.Serialization; -namespace Slim.Exceptions +namespace Slim.Exceptions; + +/// +/// Exception thrown when fails to create a service. +/// +public class DependencyInjectionException : Exception { /// - /// Exception thrown when fails to create a service. + /// Creates a new instance of . /// - public class DependencyInjectionException : Exception + public DependencyInjectionException() { - /// - /// Creates a new instance of . - /// - public DependencyInjectionException() - { - } + } - /// - /// Creates a new instance of . - /// - /// Message of the exception. - public DependencyInjectionException(string message) : base(message) - { - } + /// + /// Creates a new instance of . + /// + /// Message of the exception. + public DependencyInjectionException(string message) : base(message) + { + } - /// - /// Creates a new instance of . - /// - /// Message of the exception. - /// Inner exception. - public DependencyInjectionException(string message, Exception innerException) : base(message, innerException) - { - } + /// + /// Creates a new instance of . + /// + /// Message of the exception. + /// Inner exception. + public DependencyInjectionException(string message, Exception innerException) : base(message, innerException) + { + } - /// - /// Creates a new instance of . - /// - /// Serialization info. - /// Streaming context. - protected DependencyInjectionException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } + /// + /// Creates a new instance of . + /// + /// Serialization info. + /// Streaming context. + protected DependencyInjectionException(SerializationInfo info, StreamingContext context) : base(info, context) + { } } diff --git a/Slim/IServiceManager.cs b/Slim/IServiceManager.cs index 7575a27..9a58e09 100644 --- a/Slim/IServiceManager.cs +++ b/Slim/IServiceManager.cs @@ -1,40 +1,43 @@ using System; using System.Collections.Generic; -namespace Slim +namespace Slim; + +/// +/// Interface for . +/// +public interface IServiceManager : IServiceProvider, IServiceProducer { /// - /// Interface for . + /// Gets the that created the current . Returns null in case there is no parent. /// - public interface IServiceManager : IServiceProvider, IServiceProducer - { - /// - /// Allow modifications to scoped created from this . - /// - bool AllowScopedManagerModifications { get; set; } - /// - /// Returns true if is readonly. - /// - bool IsReadOnly { get; } - /// - /// Builds all registered singletons. - /// - void BuildSingletons(); - /// - /// Marks a type of exception to be caught and handled. - /// - /// Type of exception to catch. - /// Handler of the exception. Handler returns true if the exception should be thrown again. - /// - /// - void HandleException(Func handle) - where T : Exception; - /// - /// Clears all registered types, singletons, factories and exception handlers. - /// - /// - /// Calls on all singletons. - /// - void Clear(); - } + IServiceManager ParentServiceManager { get; } + /// + /// Allow modifications to scoped created from this . + /// + bool AllowScopedManagerModifications { get; set; } + /// + /// Returns true if is readonly. + /// + bool IsReadOnly { get; } + /// + /// Builds all registered singletons. + /// + void BuildSingletons(); + /// + /// Marks a type of exception to be caught and handled. + /// + /// Type of exception to catch. + /// Handler of the exception. Handler returns true if the exception should be thrown again. + /// + /// + void HandleException(Func handle) + where T : Exception; + /// + /// Clears all registered types, singletons, factories and exception handlers. + /// + /// + /// Calls on all singletons. + /// + void Clear(); } diff --git a/Slim/IServiceProducer.cs b/Slim/IServiceProducer.cs index 043b1b4..d2a6e8e 100644 --- a/Slim/IServiceProducer.cs +++ b/Slim/IServiceProducer.cs @@ -1,256 +1,255 @@ using Slim.Resolvers; using System; -namespace Slim +namespace Slim; + +/// +/// Interface allowing to produce services for the . +/// +public interface IServiceProducer { /// - /// Interface allowing to produce services for the . + /// Register the current service manager as a valid dependency. /// - public interface IServiceProducer - { - /// - /// Register the current service manager as a valid dependency. - /// - /// Registers current service manager as , , , . - void RegisterServiceManager(); - /// - /// Returns true if there exists a registration for . - /// - /// Type of registered service. - /// True if service is registered. Otherwise returns false. - bool IsRegistered(Type tInterface); - /// - /// Returns true if there exists a registration for . - /// - /// Type of registered service. - /// True if service is registered. Otherwise returns false. - bool IsRegistered(); - /// - /// Register a resolver that manually resolves dependencies. - /// - /// Resolver that manually creates a dependency. - void RegisterResolver(IDependencyResolver dependencyResolver); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// Type of implementation. - /// If true, will register all interfaces implemented by the provided class./> - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Func serviceFactory, bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Func serviceFactory, bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Func serviceFactory, bool registerAllInterfaces = false) - where TClass : class; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient() - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Func serviceFactory) - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton() - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Func serviceFactory) - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped() - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Func serviceFactory) - where TInterface : class - where TClass : TInterface; - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Type tInterface, Type tClass); - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Type tInterface, Type tClass, Func serviceFactory); - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Type tInterface, Type tClass); - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Type tInterface, Type tClass, Func serviceFactory); - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Type tInterface, Type tClass); - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Type tInterface, Type tClass, Func serviceFactory); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Type tClass, bool registerAllInterfaces = false); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterTransient(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Type tClass, bool registerAllInterfaces = false); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterSingleton(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Type tClass, bool registerAllInterfaces = false); - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - void RegisterScoped(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); - } + /// Registers current service manager as , , , . + void RegisterServiceManager(); + /// + /// Returns true if there exists a registration for . + /// + /// Type of registered service. + /// True if service is registered. Otherwise returns false. + bool IsRegistered(Type tInterface); + /// + /// Returns true if there exists a registration for . + /// + /// Type of registered service. + /// True if service is registered. Otherwise returns false. + bool IsRegistered(); + /// + /// Register a resolver that manually resolves dependencies. + /// + /// Resolver that manually creates a dependency. + void RegisterResolver(IDependencyResolver dependencyResolver); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// Type of implementation. + /// If true, will register all interfaces implemented by the provided class./> + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Func serviceFactory, bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Func serviceFactory, bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Func serviceFactory, bool registerAllInterfaces = false) + where TClass : class; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient() + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Func serviceFactory) + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton() + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Func serviceFactory) + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped() + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Func serviceFactory) + where TInterface : class + where TClass : TInterface; + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Type tInterface, Type tClass); + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Type tInterface, Type tClass, Func serviceFactory); + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Type tInterface, Type tClass); + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Type tInterface, Type tClass, Func serviceFactory); + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Type tInterface, Type tClass); + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Type tInterface, Type tClass, Func serviceFactory); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Type tClass, bool registerAllInterfaces = false); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterTransient(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Type tClass, bool registerAllInterfaces = false); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterSingleton(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Type tClass, bool registerAllInterfaces = false); + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + void RegisterScoped(Type tClass, Func serviceFactory, bool registerAllInterfaces = false); } diff --git a/Slim/IServiceProvider.cs b/Slim/IServiceProvider.cs index 7bcf523..1782f6a 100644 --- a/Slim/IServiceProvider.cs +++ b/Slim/IServiceProvider.cs @@ -2,34 +2,33 @@ using System.Collections.Generic; using Slim.Exceptions; -namespace Slim +namespace Slim; + +/// +/// Interface allowing to request services from the . +/// +public interface IServiceProvider : System.IServiceProvider, IDisposable { /// - /// Interface allowing to request services from the . + /// Returns all services that can be cast to provided type. /// - public interface IServiceProvider : System.IServiceProvider, IDisposable - { - /// - /// Returns all services that can be cast to provided type. - /// - /// Type of the returned services. - IEnumerable GetServicesOfType(); - /// - /// Returns all services that can be cast to provided type. - /// - /// Type of the returned services. - IEnumerable GetServicesOfType(Type type); - /// - /// Creates a scoped . - /// - /// Scoped . - IServiceProvider CreateScope(); - /// - /// Resolves and returns the required service. - /// - /// Type of required service. - /// Required service. - /// Thrown when unable to resolve required service. - TInterface GetService() where TInterface : class; - } + /// Type of the returned services. + IEnumerable GetServicesOfType(); + /// + /// Returns all services that can be cast to provided type. + /// + /// Type of the returned services. + IEnumerable GetServicesOfType(Type type); + /// + /// Creates a scoped . + /// + /// Scoped . + IServiceProvider CreateScope(); + /// + /// Resolves and returns the required service. + /// + /// Type of required service. + /// Required service. + /// Thrown when unable to resolve required service. + TInterface GetService() where TInterface : class; } diff --git a/Slim/Lifetime.cs b/Slim/Lifetime.cs index f0da58a..80dbc89 100644 --- a/Slim/Lifetime.cs +++ b/Slim/Lifetime.cs @@ -1,9 +1,8 @@ -namespace Slim +namespace Slim; + +internal enum Lifetime { - internal enum Lifetime - { - Transient, - Singleton, - Scoped - } + Transient, + Singleton, + Scoped } diff --git a/Slim/Resolvers/IDependencyResolver.cs b/Slim/Resolvers/IDependencyResolver.cs index 410b884..f7d4c21 100644 --- a/Slim/Resolvers/IDependencyResolver.cs +++ b/Slim/Resolvers/IDependencyResolver.cs @@ -1,24 +1,23 @@ using System; -namespace Slim.Resolvers +namespace Slim.Resolvers; + +/// +/// Resolver that can be added to a to manually resolve a dependency. +/// +public interface IDependencyResolver { /// - /// Resolver that can be added to a to manually resolve a dependency. + /// Returns true if the can resolve the dependency of provided . /// - public interface IDependencyResolver - { - /// - /// Returns true if the can resolve the dependency of provided . - /// - /// of dependency to be resolved. - /// True if can resolve the dependency. Otherwise false. - bool CanResolve(Type type); - /// - /// Returns a resolved dependency of the provided type. - /// - /// Reference to the calling . - /// of the dependency to be resolved. - /// Resolved dependency of provided . - object Resolve(IServiceProvider serviceProvider, Type type); - } + /// of dependency to be resolved. + /// True if can resolve the dependency. Otherwise false. + bool CanResolve(Type type); + /// + /// Returns a resolved dependency of the provided type. + /// + /// Reference to the calling . + /// of the dependency to be resolved. + /// Resolved dependency of provided . + object Resolve(IServiceProvider serviceProvider, Type type); } diff --git a/Slim/ServiceManager.cs b/Slim/ServiceManager.cs index 3076581..4503848 100644 --- a/Slim/ServiceManager.cs +++ b/Slim/ServiceManager.cs @@ -6,896 +6,908 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace Slim +namespace Slim; + +/// +/// responsible with storing and resolving services. +/// +public sealed class ServiceManager : IServiceManager { + private bool disposedValue; + private readonly bool scoped = false; + + private Dictionary InterfaceMapping { get; } = new Dictionary(); + private Dictionary Instances { get; } = new Dictionary(); + private Dictionary Factories { get; } = new Dictionary(); + private Dictionary ExceptionHandlers { get; } = new Dictionary(); + private List Resolvers { get; } = new List(); + /// - /// responsible with storing and resolving services. + /// Allow modifications to scoped created from this . /// - public sealed class ServiceManager : IServiceManager + public bool AllowScopedManagerModifications { get; set; } + /// + /// Returns true if is readonly. + /// + public bool IsReadOnly { get; } + /// + /// Gets the that created the current . Returns null in case there is no parent. + /// + public IServiceManager ParentServiceManager { get; } + + /// + /// Creates an instance of . + /// + public ServiceManager() { - private bool disposedValue; - private readonly bool scoped = false; + } - private Dictionary InterfaceMapping { get; } = new Dictionary(); - private Dictionary Instances { get; } = new Dictionary(); - private Dictionary Factories { get; } = new Dictionary(); - private Dictionary ExceptionHandlers { get; } = new Dictionary(); - private List Resolvers { get; } = new List(); + private ServiceManager( + Dictionary interfaceMapping, + Dictionary singletons, + Dictionary factories, + Dictionary exceptionHandlers, + List resolvers, + IServiceManager parent, + bool isReadOnly) + { + this.InterfaceMapping = interfaceMapping; + this.Instances = singletons; + this.Factories = factories; + this.ExceptionHandlers = exceptionHandlers; + this.Resolvers = resolvers; + this.scoped = true; + this.ParentServiceManager = parent; + this.IsReadOnly = isReadOnly; + } - /// - /// Allow modifications to scoped created from this . - /// - public bool AllowScopedManagerModifications { get; set; } - /// - /// Returns true if is readonly. - /// - public bool IsReadOnly { get; } - - /// - /// Creates an instance of . - /// - public ServiceManager() + /// + /// Returns true if there exists a registration for . + /// + /// Type of registered service. + /// True if service is registered. Otherwise returns false. + public bool IsRegistered(Type tInterface) + { + return this.IsMapped(tInterface); + } + /// + /// Returns true if there exists a registration for . + /// + /// Type of registered service. + /// True if service is registered. Otherwise returns false. + public bool IsRegistered() + { + return this.IsMapped(typeof(T)); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient() + where TInterface : class + where TClass : TInterface + { + this.RegisterService(typeof(TClass), Lifetime.Transient, typeof(TInterface)); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Func serviceFactory) + where TInterface : class + where TClass : TInterface + { + if (serviceFactory is null) { + throw new ArgumentNullException(nameof(serviceFactory)); } - private ServiceManager( - Dictionary interfaceMapping, - Dictionary singletons, - Dictionary factories, - Dictionary exceptionHandlers, - List resolvers, - bool isReadOnly) + this.RegisterService(typeof(TClass), Lifetime.Transient, typeof(TInterface), serviceFactory); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton() + where TInterface : class + where TClass : TInterface + { + this.RegisterService(typeof(TClass), Lifetime.Singleton, typeof(TInterface)); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Func serviceFactory) + where TInterface : class + where TClass : TInterface + { + if (serviceFactory is null) { - this.InterfaceMapping = interfaceMapping; - this.Instances = singletons; - this.Factories = factories; - this.ExceptionHandlers = exceptionHandlers; - this.Resolvers = resolvers; - this.scoped = true; - this.IsReadOnly = isReadOnly; + throw new ArgumentNullException(nameof(serviceFactory)); } - /// - /// Returns true if there exists a registration for . - /// - /// Type of registered service. - /// True if service is registered. Otherwise returns false. - public bool IsRegistered(Type tInterface) + this.RegisterService(typeof(TClass), Lifetime.Singleton, typeof(TInterface), serviceFactory); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped() + where TInterface : class + where TClass : TInterface + { + this.RegisterService(typeof(TClass), Lifetime.Scoped, typeof(TInterface)); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Func serviceFactory) + where TInterface : class + where TClass : TInterface + { + if (serviceFactory is null) { - return this.IsMapped(tInterface); + throw new ArgumentNullException(nameof(serviceFactory)); } - /// - /// Returns true if there exists a registration for . - /// - /// Type of registered service. - /// True if service is registered. Otherwise returns false. - public bool IsRegistered() + + this.RegisterService(typeof(TClass), Lifetime.Scoped, typeof(TInterface), serviceFactory); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Type tInterface, Type tClass) + { + this.RegisterService(tClass, Lifetime.Transient, tInterface); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Type tInterface, Type tClass, Func serviceFactory) + { + if (serviceFactory is null) { - return this.IsMapped(typeof(T)); + throw new ArgumentNullException(nameof(serviceFactory)); } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient() - where TInterface : class - where TClass : TInterface + + this.RegisterService(tClass, Lifetime.Transient, tInterface, serviceFactory); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Type tInterface, Type tClass) + { + this.RegisterService(tClass, Lifetime.Singleton, tInterface); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Type tInterface, Type tClass, Func serviceFactory) + { + if (serviceFactory is null) { - this.RegisterService(typeof(TClass), Lifetime.Transient, typeof(TInterface)); + throw new ArgumentNullException(nameof(serviceFactory)); } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Func serviceFactory) - where TInterface : class - where TClass : TInterface + + this.RegisterService(tClass, Lifetime.Singleton, tInterface, serviceFactory); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Type tInterface, Type tClass) + { + this.RegisterService(tClass, Lifetime.Scoped, tInterface); + } + /// + /// Register a service into with . + /// + /// Type of interface. + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Type tInterface, Type tClass, Func serviceFactory) + { + if (serviceFactory is null) { - if (serviceFactory is null) + throw new ArgumentNullException(nameof(serviceFactory)); + } + + this.RegisterService(tClass, Lifetime.Scoped, tInterface, serviceFactory); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(bool registerAllInterfaces = false) where TClass : class + { + this.RegisterService(typeof(TClass), Lifetime.Singleton, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class + { + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } + + this.RegisterService(typeof(TClass), Lifetime.Singleton, null, serviceFactory, registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(bool registerAllInterfaces = false) where TClass : class + { + this.RegisterService(typeof(TClass), Lifetime.Transient, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class + { + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } + + this.RegisterService(typeof(TClass), Lifetime.Transient, null, serviceFactory, registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(bool registerAllInterfaces = false) where TClass : class + { + this.RegisterService(typeof(TClass), Lifetime.Scoped, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for the implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class + { + if (serviceFactory is null) + { + throw new ArgumentNullException(nameof(serviceFactory)); + } + + this.RegisterService(typeof(TClass), Lifetime.Scoped, null, serviceFactory, registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Type tClass, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Transient, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterTransient(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Transient, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Type tClass, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Singleton, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterSingleton(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Singleton, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Type tClass, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Scoped, registerAllInterfaces: registerAllInterfaces); + } + /// + /// Register a service into with . + /// Registers the service for all interfaces it implements. + /// + /// If true, will register all interfaces implemented by the provided class./> + /// Type of implementation. + /// Factory for implementation. + /// Thrown when the provided serviceFactory is null. + /// Thrown when contains an entry for the provided interface type. + public void RegisterScoped(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) + { + this.RegisterService(tClass, Lifetime.Scoped, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); + } + + /// + /// Register the current service manager as a valid dependency. + /// + /// Registers current service manager as , , , . + public void RegisterServiceManager() + { + this.RegisterSingleton(sp => this); + this.RegisterSingleton(sp => this); + this.RegisterSingleton(sp => this); + this.RegisterSingleton(sp => this); + } + + /// + /// Creates a scoped . + /// + /// Scoped . + public IServiceProvider CreateScope() + { + return this.CreateScopeInner(); + } + /// + /// Resolves and returns the required service. + /// + /// Type of required service. + /// Required service. + /// Thrown when unable to resolve required service. + public TInterface GetService() where TInterface : class + { + return this.PrepareAndGetService(typeof(TInterface)) as TInterface; + } + /// + /// Resolves and returns the required service. + /// + /// Type of required service. + /// Required service. + /// Thrown when unable to resolve required service. + public object GetService(Type type) + { + return this.PrepareAndGetService(type); + } + /// + /// Returns all services that can be cast to provided type. + /// + /// Type of the returned services. + public IEnumerable GetServicesOfType() + { + return this.EnumerateAndReturnServicesOfType(typeof(T)).OfType(); + } + /// + /// Returns all services that can be cast to provided type. + /// + /// Type of the returned services. + public IEnumerable GetServicesOfType(Type type) + { + return this.EnumerateAndReturnServicesOfType(type); + } + /// + /// Marks a type of exception to be caught and handled. + /// + /// Type of exception to catch. + /// Handler of the exception. Handler returns true if the exception should be thrown again. + /// + /// + public void HandleException(Func handle) + where T : Exception + { + this.ExceptionHandlers.Add(typeof(T), handle); + } + /// + /// Clears all registered types, singletons, factories and exception handlers. + /// + /// + /// Calls on all singletons. + /// + public void Clear() + { + if (this.IsReadOnly) + { + throw new InvalidOperationException($"Cannot clear {nameof(ServiceManager)}. {nameof(ServiceManager)} is readonly!"); + } + + this.InterfaceMapping.Clear(); + foreach (var singleton in this.Instances.Values.Where(s => s is IDisposable).Select(s => (IDisposable)s)) + { + singleton.Dispose(); + } + + this.Factories.Clear(); + this.ExceptionHandlers.Clear(); + } + /// + /// Build all registered singletons. + /// + public void BuildSingletons() + { + foreach (var mapping in this.InterfaceMapping.Where(map => map.Value.Item2 == Lifetime.Singleton)) + { + this.GetService(mapping.Key); + } + } + /// + /// Register a that manually resolves dependencies. + /// + /// + /// This resolver will be called before auto-resolving a dependency. If resolver can handle the dependency, will return the resolved value. + /// + /// that manually creates a dependency. + public void RegisterResolver(IDependencyResolver dependencyResolver) + { + if (this.IsReadOnly) + { + throw new InvalidOperationException($"Cannot register resolver. {nameof(ServiceManager)} is readonly!"); + } + + this.Resolvers.Add(dependencyResolver); + } + /// + /// Cleans up resources and disposes of all disposable singletons. + /// + public void Dispose() + { + this.Dispose(disposing: true); + } + + private IEnumerable EnumerateAndReturnServicesOfType(Type type) + { + foreach (var kvp in this.InterfaceMapping) + { + var objectType = kvp.Value.Item1; + if (type.IsAssignableFrom(objectType)) { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Transient, typeof(TInterface), serviceFactory); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton() - where TInterface : class - where TClass : TInterface - { - this.RegisterService(typeof(TClass), Lifetime.Singleton, typeof(TInterface)); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Func serviceFactory) - where TInterface : class - where TClass : TInterface - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Singleton, typeof(TInterface), serviceFactory); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped() - where TInterface : class - where TClass : TInterface - { - this.RegisterService(typeof(TClass), Lifetime.Scoped, typeof(TInterface)); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Func serviceFactory) - where TInterface : class - where TClass : TInterface - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Scoped, typeof(TInterface), serviceFactory); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Type tInterface, Type tClass) - { - this.RegisterService(tClass, Lifetime.Transient, tInterface); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Type tInterface, Type tClass, Func serviceFactory) - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(tClass, Lifetime.Transient, tInterface, serviceFactory); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Type tInterface, Type tClass) - { - this.RegisterService(tClass, Lifetime.Singleton, tInterface); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Type tInterface, Type tClass, Func serviceFactory) - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(tClass, Lifetime.Singleton, tInterface, serviceFactory); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Type tInterface, Type tClass) - { - this.RegisterService(tClass, Lifetime.Scoped, tInterface); - } - /// - /// Register a service into with . - /// - /// Type of interface. - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Type tInterface, Type tClass, Func serviceFactory) - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(tClass, Lifetime.Scoped, tInterface, serviceFactory); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(bool registerAllInterfaces = false) where TClass : class - { - this.RegisterService(typeof(TClass), Lifetime.Singleton, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Singleton, null, serviceFactory, registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(bool registerAllInterfaces = false) where TClass : class - { - this.RegisterService(typeof(TClass), Lifetime.Transient, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Transient, null, serviceFactory, registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(bool registerAllInterfaces = false) where TClass : class - { - this.RegisterService(typeof(TClass), Lifetime.Scoped, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for the implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Func serviceFactory, bool registerAllInterfaces = false) where TClass : class - { - if (serviceFactory is null) - { - throw new ArgumentNullException(nameof(serviceFactory)); - } - - this.RegisterService(typeof(TClass), Lifetime.Scoped, null, serviceFactory, registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Type tClass, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Transient, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterTransient(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Transient, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Type tClass, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Singleton, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterSingleton(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Singleton, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Type tClass, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Scoped, registerAllInterfaces: registerAllInterfaces); - } - /// - /// Register a service into with . - /// Registers the service for all interfaces it implements. - /// - /// If true, will register all interfaces implemented by the provided class./> - /// Type of implementation. - /// Factory for implementation. - /// Thrown when the provided serviceFactory is null. - /// Thrown when contains an entry for the provided interface type. - public void RegisterScoped(Type tClass, Func serviceFactory, bool registerAllInterfaces = false) - { - this.RegisterService(tClass, Lifetime.Scoped, null, serviceFactory, registerAllInterfaces: registerAllInterfaces); - } - - /// - /// Register the current service manager as a valid dependency. - /// - /// Registers current service manager as , , , . - public void RegisterServiceManager() - { - this.RegisterSingleton(sp => this); - this.RegisterSingleton(sp => this); - this.RegisterSingleton(sp => this); - this.RegisterSingleton(sp => this); - } - - /// - /// Creates a scoped . - /// - /// Scoped . - public IServiceProvider CreateScope() - { - return this.CreateScopeInner(); - } - /// - /// Resolves and returns the required service. - /// - /// Type of required service. - /// Required service. - /// Thrown when unable to resolve required service. - public TInterface GetService() where TInterface : class - { - return this.PrepareAndGetService(typeof(TInterface)) as TInterface; - } - /// - /// Resolves and returns the required service. - /// - /// Type of required service. - /// Required service. - /// Thrown when unable to resolve required service. - public object GetService(Type type) - { - return this.PrepareAndGetService(type); - } - /// - /// Returns all services that can be cast to provided type. - /// - /// Type of the returned services. - public IEnumerable GetServicesOfType() - { - return this.EnumerateAndReturnServicesOfType(typeof(T)).OfType(); - } - /// - /// Returns all services that can be cast to provided type. - /// - /// Type of the returned services. - public IEnumerable GetServicesOfType(Type type) - { - return this.EnumerateAndReturnServicesOfType(type); - } - /// - /// Marks a type of exception to be caught and handled. - /// - /// Type of exception to catch. - /// Handler of the exception. Handler returns true if the exception should be thrown again. - /// - /// - public void HandleException(Func handle) - where T : Exception - { - this.ExceptionHandlers.Add(typeof(T), handle); - } - /// - /// Clears all registered types, singletons, factories and exception handlers. - /// - /// - /// Calls on all singletons. - /// - public void Clear() - { - if (this.IsReadOnly) - { - throw new InvalidOperationException($"Cannot clear {nameof(ServiceManager)}. {nameof(ServiceManager)} is readonly!"); - } - - this.InterfaceMapping.Clear(); - foreach(var singleton in this.Instances.Values.Where(s => s is IDisposable).Select(s => (IDisposable)s)) - { - singleton.Dispose(); - } - - this.Factories.Clear(); - this.ExceptionHandlers.Clear(); - } - /// - /// Build all registered singletons. - /// - public void BuildSingletons() - { - foreach(var mapping in this.InterfaceMapping.Where(map => map.Value.Item2 == Lifetime.Singleton)) - { - this.GetService(mapping.Key); + yield return this.PrepareAndGetService(kvp.Key); } } - /// - /// Register a that manually resolves dependencies. - /// - /// - /// This resolver will be called before auto-resolving a dependency. If resolver can handle the dependency, will return the resolved value. - /// - /// that manually creates a dependency. - public void RegisterResolver(IDependencyResolver dependencyResolver) + } + private void RegisterService(Type tClass, Lifetime lifetime, Type tInterface = null, Delegate serviceFactory = null, bool registerAllInterfaces = false) + { + if (this.IsReadOnly) { - if (this.IsReadOnly) - { - throw new InvalidOperationException($"Cannot register resolver. {nameof(ServiceManager)} is readonly!"); - } - - this.Resolvers.Add(dependencyResolver); - } - /// - /// Cleans up resources and disposes of all disposable singletons. - /// - public void Dispose() - { - this.Dispose(disposing: true); + throw new InvalidOperationException($"Cannot register service. {nameof(ServiceManager)} is readonly!"); } - private IEnumerable EnumerateAndReturnServicesOfType(Type type) + if (tInterface is not null) { - foreach(var kvp in this.InterfaceMapping) - { - var objectType = kvp.Value.Item1; - if (type.IsAssignableFrom(objectType)) - { - yield return this.PrepareAndGetService(kvp.Key); - } - } - } - private void RegisterService(Type tClass, Lifetime lifetime, Type tInterface = null, Delegate serviceFactory = null, bool registerAllInterfaces = false) - { - if (this.IsReadOnly) - { - throw new InvalidOperationException($"Cannot register service. {nameof(ServiceManager)} is readonly!"); - } - - if (tInterface is not null) - { - this.Map(tInterface, tClass, lifetime); - if (serviceFactory is not null) - { - this.RegisterFactory(tInterface, serviceFactory); - } - - return; - } - - if (registerAllInterfaces) - { - foreach (var i in tClass.GetInterfaces()) - { - this.Map(i, tClass, lifetime); - if (serviceFactory is not null) - { - this.RegisterFactory(i, serviceFactory); - } - } - } - - this.Map(tClass, tClass, lifetime); + this.Map(tInterface, tClass, lifetime); if (serviceFactory is not null) { - this.RegisterFactory(tClass, serviceFactory); - } - } - private object PrepareAndGetService(Type tInterface) - { - return this.TryFunc(() => - { - return this.GetObject(tInterface); - }); - } - private void RegisterFactory(Type tinterface, Delegate factory) - { - this.TryAction(() => - { - lock (this) - { - this.Factories[tinterface] = factory; - } - }); - } - private void Map(Type tinterface, Type tclass, Lifetime lifetime) - { - this.TryAction(() => - { - lock (this) - { - if (this.InterfaceMapping.ContainsKey(tinterface)) - { - throw new InvalidOperationException($"{nameof(ServiceManager)} already contains an entry for type {tinterface}"); - } - - this.InterfaceMapping[tinterface] = (tclass, lifetime); - } - }); - } - private bool IsMapped(Type tinterface) - { - return this.TryFunc(() => - { - lock (this) - { - if (this.Resolvers.Any(resolver => resolver.CanResolve(tinterface))) - { - return true; - } - - return this.InterfaceMapping.ContainsKey(tinterface); - } - }); - } - private object GetObject(Type tInterface) - { - if (!this.InterfaceMapping.TryGetValue(tInterface, out var mappingTuple) && - this.Resolvers.Any(resolver => resolver.CanResolve(tInterface)) is false) - { - throw new DependencyInjectionException($"No registered service and no resolver can resolve for type {tInterface.Name}."); + this.RegisterFactory(tInterface, serviceFactory); } - (var type, var lifetime) = mappingTuple; - object obj; - if (lifetime == Lifetime.Singleton || lifetime == Lifetime.Scoped) + return; + } + + if (registerAllInterfaces) + { + foreach (var i in tClass.GetInterfaces()) { - if (!this.Instances.TryGetValue(type, out obj)) + this.Map(i, tClass, lifetime); + if (serviceFactory is not null) { - obj = this.TryImplementService(tInterface); + this.RegisterFactory(i, serviceFactory); } } - else + } + + this.Map(tClass, tClass, lifetime); + if (serviceFactory is not null) + { + this.RegisterFactory(tClass, serviceFactory); + } + } + private object PrepareAndGetService(Type tInterface) + { + return this.TryFunc(() => + { + return this.GetObject(tInterface); + }); + } + private void RegisterFactory(Type tinterface, Delegate factory) + { + this.TryAction(() => + { + lock (this) + { + this.Factories[tinterface] = factory; + } + }); + } + private void Map(Type tinterface, Type tclass, Lifetime lifetime) + { + this.TryAction(() => + { + lock (this) + { + if (this.InterfaceMapping.ContainsKey(tinterface)) + { + throw new InvalidOperationException($"{nameof(ServiceManager)} already contains an entry for type {tinterface}"); + } + + this.InterfaceMapping[tinterface] = (tclass, lifetime); + } + }); + } + private bool IsMapped(Type tinterface) + { + return this.TryFunc(() => + { + lock (this) + { + if (this.Resolvers.Any(resolver => resolver.CanResolve(tinterface))) + { + return true; + } + + return this.InterfaceMapping.ContainsKey(tinterface); + } + }); + } + private object GetObject(Type tInterface) + { + if (!this.InterfaceMapping.TryGetValue(tInterface, out var mappingTuple) && + this.Resolvers.Any(resolver => resolver.CanResolve(tInterface)) is false) + { + throw new DependencyInjectionException($"No registered service and no resolver can resolve for type {tInterface.Name}."); + } + + (var type, var lifetime) = mappingTuple; + object obj; + if (lifetime == Lifetime.Singleton || lifetime == Lifetime.Scoped) + { + if (!this.Instances.TryGetValue(type, out obj)) { obj = this.TryImplementService(tInterface); } + } + else + { + obj = this.TryImplementService(tInterface); + } - if (obj is not null) + if (obj is not null) + { + if (lifetime == Lifetime.Singleton || lifetime == Lifetime.Scoped) { - if (lifetime == Lifetime.Singleton || lifetime == Lifetime.Scoped) + this.Instances[type] = obj; + } + + return obj; + } + + /* + * If no constructors were able to be called succesfully, throw exception. + */ + + throw new DependencyInjectionException($"No suitable constructor was found for type {tInterface.Name}!"); + } + private object TryImplementService(Type type) + { + foreach (var resolver in this.Resolvers) + { + if (resolver.CanResolve(type)) + { + return resolver.Resolve(this, type); + } + } + + (var implementType, _) = this.InterfaceMapping[type]; + if (this.Factories.TryGetValue(type, out var factory)) + { + return factory.DynamicInvoke(this); + } + + return this.FindAndCallConstructors(implementType); + } + private object FindAndCallConstructors(Type implementType) + { + /* + * Order constructors to give priority to constructors that have PrefferedConstructorAttribute decorator + */ + var constructors = implementType.GetConstructors() + .Where(c => c.GetCustomAttribute() is null) + .OrderBy(c => + c.GetCustomAttribute() is PreferredConstructorAttribute preferredConstructorAttribute ? + preferredConstructorAttribute.Priority : + int.MaxValue); + foreach (var constructor in constructors) + { + /* + * Filter by public constructors and try to find if the DI Service has + * implementations for the required parameters. If there are, add them to a list and call + * the constructor with the paramters. + * If the parameters are missing, try with other constructors. + */ + + var parameters = this.GetParameters(constructor.GetParameters()); + if (parameters is null) + { + continue; + } + + return constructor.Invoke(parameters); + } + + return null; + } + private object[] GetParameters(ParameterInfo[] parameterInfos) + { + var parameterImplementationList = new List(); + foreach (var par in parameterInfos) + { + if (!this.InterfaceMapping.TryGetValue(par.ParameterType, out var mappingTuple)) + { + object resolvedParam = null; + foreach (var resolver in this.Resolvers) { - this.Instances[type] = obj; + if (resolver.CanResolve(par.ParameterType)) + { + resolvedParam = resolver.Resolve(this, par.ParameterType); + continue; + } } + if (resolvedParam is not null) + { + parameterImplementationList.Add(resolvedParam); + continue; + } + + return null; + } + + (var actualType, var lifetime) = mappingTuple; + if (this.Instances.TryGetValue(par.ParameterType, out var obj)) + { + parameterImplementationList.Add(obj); + continue; + } + + if (this.Instances.TryGetValue(actualType, out obj)) + { + parameterImplementationList.Add(obj); + continue; + } + + obj = this.TryImplementService(par.ParameterType); + if (obj is null) + { + return null; + } + + if (lifetime == Lifetime.Singleton) + { + this.Instances[actualType] = obj; + } + + parameterImplementationList.Add(obj); + } + + return parameterImplementationList.ToArray(); + } + private ServiceManager CreateScopeInner() + { + var interfaceMapping = new Dictionary(this.InterfaceMapping); + var factories = new Dictionary(this.Factories); + var exceptionHandlers = new Dictionary(this.ExceptionHandlers); + var resolvers = new List(this.Resolvers); + /* + * For singletons, create a factory in scoped service manager that should call this manager + * to retrieve the singleton. + * This means that if the singleton has not yet been created in this service manager, + * the scoped service manager will not create a new one but instead ask the original service manager to + * create the singleton instead. + */ + foreach (var kvp in this.InterfaceMapping) + { + if (kvp.Value.Item2 is Lifetime.Singleton) + { + factories[kvp.Key] = new Func((scopedManager) => this.ResolveSingletonWithScopedManager(scopedManager as ServiceManager, kvp.Key, kvp.Value.Item1)); + } + } + + return new ServiceManager( + interfaceMapping: interfaceMapping, + singletons: new Dictionary(), + factories: factories, + exceptionHandlers: exceptionHandlers, + resolvers: resolvers, + parent: this, + isReadOnly: this.AllowScopedManagerModifications is false); + } + private object ResolveSingletonWithScopedManager(ServiceManager scopedManager, Type registerType, Type implementedType) + { + try + { + return this.PrepareAndGetService(registerType); + } + catch (DependencyInjectionException) + { + if (scopedManager.IsReadOnly) + { + throw; + } + + /* + * Most probably scoped manager has been modified and contains different definitions + * than the parent manager. + * As a last ditch effort, let the scoped provider try to implement the singleton. + * If successful, update the parent provider as well. + */ + var obj = scopedManager.FindAndCallConstructors(implementedType); + if (obj is not null) + { + scopedManager.Instances[implementedType] = obj; + this.Instances[implementedType] = obj; return obj; } - /* - * If no constructors were able to be called succesfully, throw exception. - */ - - throw new DependencyInjectionException($"No suitable constructor was found for type {tInterface.Name}!"); + throw; } - private object TryImplementService(Type type) + } + private void TryAction(Action action) + { + try { - foreach (var resolver in this.Resolvers) - { - if (resolver.CanResolve(type)) - { - return resolver.Resolve(this, type); - } - } - - (var implementType, _) = this.InterfaceMapping[type]; - if (this.Factories.TryGetValue(type, out var factory)) - { - return factory.DynamicInvoke(this); - } - - return this.FindAndCallConstructors(implementType); + action(); } - private object FindAndCallConstructors(Type implementType) + catch (Exception ex) { - /* - * Order constructors to give priority to constructors that have PrefferedConstructorAttribute decorator - */ - var constructors = implementType.GetConstructors() - .Where(c => c.GetCustomAttribute() is null) - .OrderBy(c => - c.GetCustomAttribute() is PreferredConstructorAttribute preferredConstructorAttribute ? - preferredConstructorAttribute.Priority : - int.MaxValue); - foreach (var constructor in constructors) - { - /* - * Filter by public constructors and try to find if the DI Service has - * implementations for the required parameters. If there are, add them to a list and call - * the constructor with the paramters. - * If the parameters are missing, try with other constructors. - */ - - var parameters = this.GetParameters(constructor.GetParameters()); - if (parameters is null) - { - continue; - } - - return constructor.Invoke(parameters); - } - - return null; + this.HandleException(ex); } - private object[] GetParameters(ParameterInfo[] parameterInfos) + } + private T TryFunc(Func action) + { + try { - var parameterImplementationList = new List(); - foreach (var par in parameterInfos) - { - if (!this.InterfaceMapping.TryGetValue(par.ParameterType, out var mappingTuple)) - { - object resolvedParam = null; - foreach (var resolver in this.Resolvers) - { - if (resolver.CanResolve(par.ParameterType)) - { - resolvedParam = resolver.Resolve(this, par.ParameterType); - continue; - } - } - - if (resolvedParam is not null) - { - parameterImplementationList.Add(resolvedParam); - continue; - } - - return null; - } - - (var actualType, var lifetime) = mappingTuple; - if (this.Instances.TryGetValue(par.ParameterType, out var obj)) - { - parameterImplementationList.Add(obj); - continue; - } - - if (this.Instances.TryGetValue(actualType, out obj)) - { - parameterImplementationList.Add(obj); - continue; - } - - obj = this.TryImplementService(par.ParameterType); - if (obj is null) - { - return null; - } - - if (lifetime == Lifetime.Singleton) - { - this.Instances[actualType] = obj; - } - - parameterImplementationList.Add(obj); - } - - return parameterImplementationList.ToArray(); + return action(); } - private ServiceManager CreateScopeInner() + catch (Exception ex) { - var interfaceMapping = new Dictionary(this.InterfaceMapping); - var factories = new Dictionary(this.Factories); - var exceptionHandlers = new Dictionary(this.ExceptionHandlers); - var resolvers = new List(this.Resolvers); - /* - * For singletons, create a factory in scoped service manager that should call this manager - * to retrieve the singleton. - * This means that if the singleton has not yet been created in this service manager, - * the scoped service manager will not create a new one but instead ask the original service manager to - * create the singleton instead. - */ - foreach (var kvp in this.InterfaceMapping) - { - if (kvp.Value.Item2 is Lifetime.Singleton) - { - factories[kvp.Key] = new Func((scopedManager) => this.ResolveSingletonWithScopedManager(scopedManager as ServiceManager, kvp.Key, kvp.Value.Item1)); - } - } - - return new ServiceManager(interfaceMapping, new Dictionary(), factories, exceptionHandlers, resolvers, this.AllowScopedManagerModifications is false); + this.HandleException(ex); } - private object ResolveSingletonWithScopedManager(ServiceManager scopedManager, Type registerType, Type implementedType) + + return default; + } + private void HandleException(Exception exception) + { + if (exception is TargetInvocationException && exception.InnerException is not null) { - try - { - return this.PrepareAndGetService(registerType); - } - catch (DependencyInjectionException) - { - if (scopedManager.IsReadOnly) - { - throw; - } - - /* - * Most probably scoped manager has been modified and contains different definitions - * than the parent manager. - * As a last ditch effort, let the scoped provider try to implement the singleton. - * If successful, update the parent provider as well. - */ - var obj = scopedManager.FindAndCallConstructors(implementedType); - if (obj is not null) - { - scopedManager.Instances[implementedType] = obj; - this.Instances[implementedType] = obj; - return obj; - } - - throw; - } + exception = exception.InnerException; } - private void TryAction(Action action) - { - try - { - action(); - } - catch (Exception ex) - { - this.HandleException(ex); - } - } - private T TryFunc(Func action) - { - try - { - return action(); - } - catch (Exception ex) - { - this.HandleException(ex); - } - return default; - } - private void HandleException(Exception exception) + if (this.ExceptionHandlers.TryGetValue(exception.GetType(), out var handler)) { - if (exception is TargetInvocationException && exception.InnerException is not null) - { - exception = exception.InnerException; - } - - if (this.ExceptionHandlers.TryGetValue(exception.GetType(), out var handler)) - { - var shouldThrow = (bool)handler.DynamicInvoke(this, exception); - if (shouldThrow is false) - { - throw exception; - } - } - else + var shouldThrow = (bool)handler.DynamicInvoke(this, exception); + if (shouldThrow is false) { throw exception; } } - private void Dispose(bool disposing) + else { - if (!this.disposedValue) - { - if (disposing) - { - /* - * Dispose of all instances. If this instance of ServiceManager is scoped, ignore the Singleton instances as they are shared - * across multiple service managers. - */ - foreach (var interfacePair in this.InterfaceMapping - .Where(kvp => kvp.Value.Item2 != Lifetime.Transient) - .Where(kvp => this.scoped is false || kvp.Value.Item2 != Lifetime.Singleton)) - { - if (this.Instances.TryGetValue(interfacePair.Key, out var instance) && instance is IDisposable disposable) - { - disposable.Dispose(); - } - else if (this.Instances.TryGetValue(interfacePair.Value.Item1, out var instance2) && instance2 is IDisposable disposable2) - { - disposable2.Dispose(); - } - } - } - - this.InterfaceMapping.Clear(); - this.Instances.Clear(); - this.Resolvers.Clear(); - this.ExceptionHandlers.Clear(); - this.Factories.Clear(); - this.disposedValue = true; - } + throw exception; } } -} \ No newline at end of file + private void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + /* + * Dispose of all instances. If this instance of ServiceManager is scoped, ignore the Singleton instances as they are shared + * across multiple service managers. + */ + foreach (var interfacePair in this.InterfaceMapping + .Where(kvp => kvp.Value.Item2 != Lifetime.Transient) + .Where(kvp => this.scoped is false || kvp.Value.Item2 != Lifetime.Singleton)) + { + if (this.Instances.TryGetValue(interfacePair.Key, out var instance) && instance is IDisposable disposable) + { + disposable.Dispose(); + } + else if (this.Instances.TryGetValue(interfacePair.Value.Item1, out var instance2) && instance2 is IDisposable disposable2) + { + disposable2.Dispose(); + } + } + } + + this.InterfaceMapping.Clear(); + this.Instances.Clear(); + this.Resolvers.Clear(); + this.ExceptionHandlers.Clear(); + this.Factories.Clear(); + this.disposedValue = true; + } + } +} diff --git a/Slim/Slim.csproj b/Slim/Slim.csproj index f0f601d..23339aa 100644 --- a/Slim/Slim.csproj +++ b/Slim/Slim.csproj @@ -8,10 +8,10 @@ Service lifetime management and dependency injection framework. - 1.8.1 - 1.8.1 + 1.9 + 1.9 https://github.com/AlexMacocian/Slim - 1.8.1 + 1.9 true true LICENSE diff --git a/Slim/Slim.xml b/Slim/Slim.xml index 4a9ee7a..b3100d3 100644 --- a/Slim/Slim.xml +++ b/Slim/Slim.xml @@ -59,6 +59,11 @@ Interface for . + + + Gets the that created the current . Returns null in case there is no parent. + + Allow modifications to scoped created from this . @@ -416,6 +421,11 @@ Returns true if is readonly. + + + Gets the that created the current . Returns null in case there is no parent. + + Creates an instance of .