Skip to content

Commit 23faa0b

Browse files
author
maximv
committed
added The cure - RegisterDelegate with the dependency parameters doc section
1 parent e467613 commit 23faa0b

File tree

4 files changed

+107
-58
lines changed

4 files changed

+107
-58
lines changed

docs/DryIoc.Docs/ErrorDetectionAndResolution.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ or via exact collection of service roots `ServiceInfo[]`.
324324
325325
## Using Validate to check for Captive Dependency
326326
327-
Captive Dependency in DI means the use of service with shorter lifespan inside a service with longet lifespan, e.g.
327+
Captive Dependency in DI means the use of service with shorter lifespan inside a service with longer lifespan, e.g.
328328
when a Scoped dependency is injected into Singleton. The problem here is that dependency with shorter livespan may be
329329
requested from the longer lived consumer when the dependency is already dead - and now you are in uncharted territory.
330330

docs/DryIoc.Docs/ErrorDetectionAndResolution.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ or via exact collection of service roots `ServiceInfo[]`.
323323

324324
## Using Validate to check for Captive Dependency
325325

326-
Captive Dependency in DI means the use of service with shorter lifespan inside a service with longet lifespan, e.g.
326+
Captive Dependency in DI means the use of service with shorter lifespan inside a service with longer lifespan, e.g.
327327
when a Scoped dependency is injected into Singleton. The problem here is that dependency with shorter livespan may be
328328
requested from the longer lived consumer when the dependency is already dead - and now you are in uncharted territory.
329329

docs/DryIoc.Docs/RegisterResolve.cs

+53-28
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
- [RegisterMany](#registermany)
1818
- [RegisterMapping](#registermapping)
1919
- [RegisterDelegate](#registerdelegate)
20-
- [Will not detect recursive dependencies](#will-not-detect-recursive-dependencies)
20+
- [The cure - RegisterDelegate with the dependency parameters](#the-cure---registerdelegate-with-the-dependency-parameters)
21+
- [RegisterDelegate is harder to use when types are not known](#registerdelegate-is-harder-to-use-when-types-are-not-known)
2122
- [RegisterInstance](#registerinstance)
2223
- [RegisterInitializer](#registerinitializer)
2324
- [RegisterPlaceholder](#registerplaceholder)
@@ -606,9 +607,9 @@ public void Example()
606607
607608
## RegisterMapping
608609
609-
`RegisterMapping` allows to map new service to already registered service and its implementation.
610+
`RegisterMapping` allows to map a new service to the already registered service and its implementation.
610611
611-
For example you have a singleton implementation which can be accessed via two different facades / services:
612+
For example you have a singleton implementation which may be accessed via two different facades / services:
612613
```cs md*/
613614
class Register_mapping
614615
{
@@ -632,7 +633,7 @@ public void Example()
632633
}/*md
633634
```
634635
635-
This same result maybe achieved via `RegisterMany`:
636+
The same result may be achieved via the `RegisterMany`:
636637
637638
```cs md*/
638639
class Register_mapping_with_RegisterMany
@@ -684,8 +685,8 @@ internal class CheerfulService : IService
684685
}/*md
685686
```
686687
687-
The `IResolverContext resolverContext` delegate parameter was not used in example above.
688-
Actually, it could be used to resolve any additional dependencies required for service creation and initialization:
688+
The `IResolverContext resolverContext` delegate parameter was not used in the example above.
689+
Actually, it could be used to resolve any additional dependencies required for the service creation and initialization:
689690
690691
```cs md*/
691692
class Register_delegate_with_resolved_dependencies
@@ -725,10 +726,54 @@ public CheerfulService(GreetingsProvider greetingsProvider)
725726
Though powerful, registering delegate may lead to the problems:
726727
727728
1. Memory leaks by capturing variables into delegate closure and keeping them for a container lifetime.
728-
2. Delegate is the black box for Container - which makes hard to find type mismatches or diagnose other potential problems.
729+
2. Delegate is the black box for Container, mostly because it should use the `Resolve` call inside to resolve the dependency cutting of the object graph analysis, which makes it hard to find type mismatches or diagnose other potential problems. Among the un-catched problems are:
730+
731+
- [Recursive Dependency](ErrorDetectionAndResolution#RecursiveDependencyDetected)
732+
- [Captive Dependency](ErrorDetectionAndResolution#using-validate-to-check-for-captive-dependency)
729733
730734
Therefore, try to use it only as a last resort. DryIoc has plenty of tools to cover for custom delegate in more effective way.
731-
The ultimate alternative would be a [FactoryMethod](ConstructorSelection).
735+
The alternative would be a [FactoryMethod](ConstructorSelection).
736+
737+
Another alternative would be the **RegisterDelegate with the dependency parameters introduced in DryIoc v4.3**. See below...
738+
739+
### The cure - RegisterDelegate with the dependency parameters
740+
741+
It solves the two problems mentioned in the [RegisterDelegate](#registerdelegate) above because
742+
it **injects** the requested dependencies as a delegate arguments so there is no need in calling `Resolve` inside.
743+
744+
- The dependencies injection and their lifetime is controlled by container
745+
- There is no black-box service location involved and both [Recursive Dependency](ErrorDetectionAndResolution#RecursiveDependencyDetected) and [Captive Dependency](ErrorDetectionAndResolution#using-validate-to-check-for-captive-dependency) problems are catched by container.
746+
747+
The example:
748+
```cs md*/
749+
750+
class Register_delegate_with_parameters
751+
{
752+
[Test] public void Example()
753+
{
754+
var container = new Container();
755+
756+
container.Register<A>(Reuse.Singleton);
757+
container.Register<B>(Reuse.Singleton);
758+
759+
// injecting A and B for X created via delegate
760+
container.RegisterDelegate<A, B, X>((a, b) => new X(a, b));
761+
762+
Assert.IsNotNull(container.Resolve<X>());
763+
}
764+
765+
class A {}
766+
class B {}
767+
class X
768+
{
769+
public X(A a, B b) {}
770+
}
771+
}
772+
773+
/*md
774+
```
775+
776+
### RegisterDelegate is harder to use when types are not known
732777
733778
Another thing, that delegate is usually hard to use when types are not known in the compile time.
734779
Given `type = typeof(Foo)` it is impossible to write `new type();`.
@@ -752,26 +797,6 @@ class Foo : IService { }
752797
/*md
753798
```
754799
755-
756-
### Will not detect recursive dependencies
757-
758-
When using normal typed registration DryIoc will detect [recursive dependencies](ErrorDetectionAndResolution#RecursiveDependencyDetected).
759-
760-
But when using the delegate registration DryIoc is unable to analyze what dependencies are used inside delegate.
761-
That is another reason to avoid `RegisterDelegate` whatsoever:
762-
```cs
763-
public class A { public A(B b) {} } // A requires B
764-
public class B { public B(A a) {} } // B requires A
765-
766-
container.Register<A>();
767-
container.RegisterDelegate<B>(r => new B(r.Resolve<A>()));
768-
769-
container.Resolve<A>(); // Fails with StackOverflowException
770-
```
771-
772-
To catch the problem with recursive dependency, replace `RegisterDelegate` with `container.Register<B>()`.
773-
774-
775800
## RegisterInstance
776801
777802
Basically `RegisterInstance` method will supply an already created external instance to the container to use it for dependency injection.

docs/DryIoc.Docs/RegisterResolve.md

+52-28
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
- [RegisterMany](#registermany)
1717
- [RegisterMapping](#registermapping)
1818
- [RegisterDelegate](#registerdelegate)
19-
- [Will not detect recursive dependencies](#will-not-detect-recursive-dependencies)
19+
- [The cure - RegisterDelegate with the dependency parameters](#the-cure---registerdelegate-with-the-dependency-parameters)
20+
- [RegisterDelegate is harder to use when types are not known](#registerdelegate-is-harder-to-use-when-types-are-not-known)
2021
- [RegisterInstance](#registerinstance)
2122
- [RegisterInitializer](#registerinitializer)
2223
- [RegisterPlaceholder](#registerplaceholder)
@@ -605,9 +606,9 @@ __Note:__ If you really need to register something from the list, you may regist
605606

606607
## RegisterMapping
607608

608-
`RegisterMapping` allows to map new service to already registered service and its implementation.
609+
`RegisterMapping` allows to map a new service to the already registered service and its implementation.
609610

610-
For example you have a singleton implementation which can be accessed via two different facades / services:
611+
For example you have a singleton implementation which may be accessed via two different facades / services:
611612
```cs
612613
class Register_mapping
613614
{
@@ -631,7 +632,7 @@ class Register_mapping
631632
}
632633
```
633634

634-
This same result maybe achieved via `RegisterMany`:
635+
The same result may be achieved via the `RegisterMany`:
635636

636637
```cs
637638
class Register_mapping_with_RegisterMany
@@ -683,8 +684,8 @@ class Register_delegate
683684
}
684685
```
685686

686-
The `IResolverContext resolverContext` delegate parameter was not used in example above.
687-
Actually, it could be used to resolve any additional dependencies required for service creation and initialization:
687+
The `IResolverContext resolverContext` delegate parameter was not used in the example above.
688+
Actually, it could be used to resolve any additional dependencies required for the service creation and initialization:
688689

689690
```cs
690691
class Register_delegate_with_resolved_dependencies
@@ -724,10 +725,53 @@ class Register_delegate_with_resolved_dependencies
724725
Though powerful, registering delegate may lead to the problems:
725726

726727
1. Memory leaks by capturing variables into delegate closure and keeping them for a container lifetime.
727-
2. Delegate is the black box for Container - which makes hard to find type mismatches or diagnose other potential problems.
728+
2. Delegate is the black box for Container, mostly because it should use the `Resolve` call inside to resolve the dependency cutting of the object graph analysis, which makes it hard to find type mismatches or diagnose other potential problems. Among the un-catched problems are:
729+
730+
- [Recursive Dependency](ErrorDetectionAndResolution#RecursiveDependencyDetected)
731+
- [Captive Dependency](ErrorDetectionAndResolution#using-validate-to-check-for-captive-dependency)
728732

729733
Therefore, try to use it only as a last resort. DryIoc has plenty of tools to cover for custom delegate in more effective way.
730-
The ultimate alternative would be a [FactoryMethod](ConstructorSelection).
734+
The alternative would be a [FactoryMethod](ConstructorSelection).
735+
736+
Another alternative would be the **RegisterDelegate with the dependency parameters introduced in DryIoc v4.3**. See below...
737+
738+
### The cure - RegisterDelegate with the dependency parameters
739+
740+
It solves the two problems mentioned in the [RegisterDelegate](#registerdelegate) above because
741+
it **injects** the requested dependencies as a delegate arguments so there is no need in calling `Resolve` inside.
742+
743+
- The dependencies injection and their lifetime is controlled by container
744+
- There is no black-box service location involved and both [Recursive Dependency](ErrorDetectionAndResolution#RecursiveDependencyDetected) and [Captive Dependency](ErrorDetectionAndResolution#using-validate-to-check-for-captive-dependency) problems are catched by container.
745+
746+
The example:
747+
```cs
748+
749+
class Register_delegate_with_parameters
750+
{
751+
[Test] public void Example()
752+
{
753+
var container = new Container();
754+
755+
container.Register<A>(Reuse.Singleton);
756+
container.Register<B>(Reuse.Singleton);
757+
758+
// injecting A and B for X created via delegate
759+
container.RegisterDelegate<A, B, X>((a, b) => new X(a, b));
760+
761+
Assert.IsNotNull(container.Resolve<X>());
762+
}
763+
764+
class A {}
765+
class B {}
766+
class X
767+
{
768+
public X(A a, B b) {}
769+
}
770+
}
771+
772+
```
773+
774+
### RegisterDelegate is harder to use when types are not known
731775

732776
Another thing, that delegate is usually hard to use when types are not known in the compile time.
733777
Given `type = typeof(Foo)` it is impossible to write `new type();`.
@@ -750,26 +794,6 @@ class Register_delegate_returning_object
750794
}
751795
```
752796

753-
754-
### Will not detect recursive dependencies
755-
756-
When using normal typed registration DryIoc will detect [recursive dependencies](ErrorDetectionAndResolution#RecursiveDependencyDetected).
757-
758-
But when using the delegate registration DryIoc is unable to analyze what dependencies are used inside delegate.
759-
That is another reason to avoid `RegisterDelegate` whatsoever:
760-
```cs
761-
public class A { public A(B b) {} } // A requires B
762-
public class B { public B(A a) {} } // B requires A
763-
764-
container.Register<A>();
765-
container.RegisterDelegate<B>(r => new B(r.Resolve<A>()));
766-
767-
container.Resolve<A>(); // Fails with StackOverflowException
768-
```
769-
770-
To catch the problem with recursive dependency, replace `RegisterDelegate` with `container.Register<B>()`.
771-
772-
773797
## RegisterInstance
774798

775799
Basically `RegisterInstance` method will supply an already created external instance to the container to use it for dependency injection.

0 commit comments

Comments
 (0)