You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enhance generated comments for roots, constructors, and Dispose methods with detailed descriptions. Add debugging and API reference guides to documentation. Improve Resolve/ResolveByTag behavior clarifications when handling root arguments.
|`DI.Setup("Composition")`| A partial class named `Composition`, unless the setup kind prevents class generation. |
116
+
|`.Root<T>("Name")`| A public property named `Name`, or a method named `Name` when the root uses root arguments or generic type arguments. |
117
+
|`.Root<T>()`| An anonymous private root. It is available only through generated `Resolve`/`ResolveByTag` methods when those methods can resolve it. |
118
+
|`.Arg<T>("name")`| A composition constructor parameter when the argument is used by at least one root graph. |
119
+
|`.RootArg<T>("name")`| A parameter on root methods that use this value. Roots with root arguments cannot be resolved by `Resolve`/`ResolveByTag`. |
120
+
|`Lifetime.Transient`| A new instance is created at each injection site. |
121
+
|`Lifetime.Singleton`| A private cached field is generated and reused by the composition. |
122
+
|`Lifetime.Scoped`| Scope-related constructors/members are generated and the instance is reused inside a scope. |
123
+
|`Lifetime.PerResolve`| A local value is reused during one root or `Resolve` call. |
124
+
|`Lifetime.PerBlock`| A local value is reused inside a generated code block. |
125
+
| Disposable singleton/scoped dependency |`Dispose` and/or `DisposeAsync` are generated on the composition. |
126
+
|`Hint.Resolve = Off`|`Resolve`/`ResolveByTag` methods and anonymous roots are not generated. Use named roots directly. |
127
+
|`Hint.ToString = On`|`ToString()` returns a Mermaid class diagram of the composition. |
128
+
|`Hint.Comments = Off`| XML documentation comments are not generated for the composition API. |
129
+
|`Hint.FormatCode = On`| Generated code is formatted for easier reading. |
130
+
131
+
</details>
132
+
61
133
<details>
62
134
<summary>Setup arguments</summary>
63
135
@@ -1523,6 +1595,17 @@ See also:
1523
1595
<summary>Comments</summary>
1524
1596
1525
1597
Pure.DI can copy comments from setup calls into generated documentation comments for the composition class, composition arguments, and composition roots.
1598
+
When no user comment is provided, Pure.DI generates documentation for the generated API so the composition can be inspected from IntelliSense.
1599
+
1600
+
Generated comments describe:
1601
+
1602
+
- The composition roots exposed by the generated composition class.
1603
+
- The root contract, implementation type, tag, and lifetime.
1604
+
- Whether a root is a property or a method.
1605
+
- Composition constructor parameters created from used `Arg<T>(...)` values.
1606
+
- Root method parameters created from used `RootArg<T>(...)` values.
1607
+
-`Resolve`/`ResolveByTag` limitations for roots that require root arguments.
1608
+
-`Dispose`/`DisposeAsync` behavior for tracked singleton and scoped disposable instances.
1526
1609
1527
1610
Use regular `//` comments before API calls when you want Pure.DI to include the text in the generated documentation:
1528
1611
@@ -1561,6 +1644,15 @@ public IService Service
1561
1644
1562
1645
For other setup calls, such as `Arg<T>(...)`, comments are used as documentation text in the generated constructor documentation. Use regular `//` comments there unless you want XML markup to be shown as text.
1563
1646
1647
+
To suppress generated documentation comments, turn comments off for the setup:
1648
+
1649
+
```c#
1650
+
DI.Setup("Composition")
1651
+
.Hint(Hint.Comments, "Off")
1652
+
.Bind<IService>().To<Service>()
1653
+
.Root<IService>("Service");
1654
+
```
1655
+
1564
1656
</details>
1565
1657
1566
1658
<details>
@@ -1599,6 +1691,65 @@ flowchart TD
1599
1691
1600
1692
</details>
1601
1693
1694
+
<details>
1695
+
<summary>Debugging generated code</summary>
1696
+
1697
+
Use this workflow when you need to inspect or debug the generated composition:
3. Rebuild the project and open the generated `Composition.g.cs` file under the configured generated files folder.
1718
+
1719
+
4. Start with the generated public API:
1720
+
1721
+
- composition constructors;
1722
+
- root properties and root methods;
1723
+
-`Resolve`/`ResolveByTag` methods;
1724
+
- scope factory methods;
1725
+
-`Dispose`/`DisposeAsync`.
1726
+
1727
+
5. Then inspect the root body that creates the graph. Constructor calls show the exact dependency path, private fields show cached singleton/scoped values, and local variables show per-resolve/per-block reuse.
1728
+
1729
+
6. For a structural view, enable `Hint.ToString` and render the Mermaid diagram:
1730
+
1731
+
```c#
1732
+
DI.Setup("Composition")
1733
+
.Hint(Hint.ToString, "On")
1734
+
.Bind<IService>().To<Service>()
1735
+
.Root<IService>("Service");
1736
+
1737
+
vardiagram=newComposition().ToString();
1738
+
```
1739
+
1740
+
7. If an anonymous root is hard to step through, disable lightweight anonymous roots for debugging:
1741
+
1742
+
```c#
1743
+
DI.Setup("Composition")
1744
+
.Hint(Hint.LightweightAnonymousRoot, "Off")
1745
+
.Bind<IService>().To<Service>()
1746
+
.Root<IService>();
1747
+
```
1748
+
1749
+
Turn `FormatCode`, `ToString`, and extra debugging hints off again when they are no longer needed.
1750
+
1751
+
</details>
1752
+
1602
1753
## Project template
1603
1754
1604
1755
Install the DI template [Pure.DI.Templates](https://www.nuget.org/packages/Pure.DI.Templates)
@@ -1676,6 +1827,62 @@ You can set project properties to save generated files and control their storage
1676
1827
1677
1828
</details>
1678
1829
1830
+
<details>
1831
+
<summary>Generated code troubleshooting</summary>
1832
+
1833
+
### Generated files are not visible
1834
+
1835
+
Set `EmitCompilerGeneratedFiles` to `true`, rebuild the project, and check the generated files folder configured by `CompilerGeneratedFilesOutputPath`. If the folder is empty, run `dotnet build-server shutdown`, rebuild, and check that the project references the `Pure.DI` package.
1836
+
1837
+
### The root was generated as a method, not a property
1838
+
1839
+
A root becomes a method when it needs runtime data, such as `RootArg<T>(...)`, or when the root itself is generic. Call it directly and pass the required arguments:
1840
+
1841
+
```c#
1842
+
varservice=composition.CreateService(userId);
1843
+
```
1844
+
1845
+
### Resolve methods were not generated
1846
+
1847
+
Check `Hint.Resolve`. When it is `Off`, `Resolve`/`ResolveByTag` methods are intentionally omitted and anonymous roots are not generated. Use named roots instead:
1848
+
1849
+
```c#
1850
+
varservice=composition.Service;
1851
+
```
1852
+
1853
+
### Resolve cannot create a root with root arguments
1854
+
1855
+
`Resolve`/`ResolveByTag` methods do not have a place to pass root arguments. Use the generated root method directly, or disable `Resolve` with `Hint.Resolve = Off` to avoid warnings and keep the generated API explicit.
1856
+
1857
+
### A lock appears in generated code
1858
+
1859
+
Pure.DI generates synchronization for thread-safe access to cached instances when needed. To remove it only when composition access is known to be single-threaded, use:
1860
+
1861
+
```c#
1862
+
DI.Setup("Composition")
1863
+
.Hint(Hint.ThreadSafe, "Off");
1864
+
```
1865
+
1866
+
### Dispose or DisposeAsync was generated
1867
+
1868
+
The composition tracks singleton and scoped instances that implement `IDisposable` or `IAsyncDisposable`. Dispose the composition when it owns such instances:
1869
+
1870
+
```c#
1871
+
usingvarcomposition=newComposition();
1872
+
```
1873
+
1874
+
or:
1875
+
1876
+
```c#
1877
+
awaitusingvarcomposition=newComposition();
1878
+
```
1879
+
1880
+
### Generated code is hard to read
1881
+
1882
+
Temporarily enable `Hint.FormatCode` and save generated files with `EmitCompilerGeneratedFiles`. For graph-level inspection, enable `Hint.ToString` and use the Mermaid diagram.
:[$"Provides a composition root of type {formatter.FormatRef(root.Node.Type)}."];
139
+
:[CreateRootDescription(root)];
140
+
141
+
stringCreateRootDescription(Rootroot)
142
+
{
143
+
vardescription=newStringBuilder();
144
+
description.Append($"Provides a composition root of type {formatter.FormatRef(root.Node.Type)}.");
145
+
if(root.RootArgs.Length>0)
146
+
{
147
+
description.Append($" This root uses root arguments and cannot be resolved by generated {hints.ResolveMethodName}/{hints.ResolveByTagMethodName} methods.");
Copy file name to clipboardExpand all lines: src/Pure.DI.Core/Core/Code/ParameterizedConstructorCommenter.cs
+3-3Lines changed: 3 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ public void AddComments(CompositionCode composition, Unit unit)
13
13
14
14
varcode=composition.Code;
15
15
code.AppendLine("/// <summary>");
16
-
code.AppendLine($"/// This parameterized constructor creates a new instance of <see cref=\"{composition.Name.ClassName}\"/> with arguments.");
16
+
code.AppendLine($"/// This parameterized constructor creates a new instance of <see cref=\"{composition.Name.ClassName}\"/> with composition arguments used by the object graph.");
@@ -34,13 +34,13 @@ public void AddComments(CompositionCode composition, Unit unit)
34
34
}
35
35
else
36
36
{
37
-
code.AppendLine($"/// <param name=\"{mdArg.ArgName}\">The composition argument of type {formatter.FormatRef(mdArg.Type)}.</param>");
37
+
code.AppendLine($"/// <param name=\"{mdArg.ArgName}\">The composition argument of type {formatter.FormatRef(mdArg.Type)}. Only arguments used by roots or their dependencies appear in this constructor.</param>");
code.AppendLine($"/// <param name=\"{arg.Name}\">The setup context of type {formatter.FormatRef(arg.Type)}.</param>");
43
+
code.AppendLine($"/// <param name=\"{arg.Name}\">The setup context argument of type {formatter.FormatRef(arg.Type)} copied from a dependent setup.</param>");
privateconststringCommentParamType="/// <param name=\"type\">The type of the composition root.</param>";
16
16
privateconststringCommentParamTag="/// <param name=\"tag\">The tag of a composition root.</param>";
@@ -176,7 +176,7 @@ public CompositionCode Build(CompositionCode composition)
176
176
privatestaticvoidFinishComments(LinesapiCode)
177
177
{
178
178
apiCode.AppendLine("/// <returns>An instance of a composition root.</returns>");
179
-
apiCode.AppendLine($"/// <exception cref=\"{Names.CannotResolveExceptionTypeName}\">Will be thrown if the corresponding composition root was not specified. To specify a composition root use API method such as <see cref=\"{Names.IConfigurationTypeName}.Root{{T}}\"/>.</exception>");
179
+
apiCode.AppendLine($"/// <exception cref=\"{Names.CannotResolveExceptionTypeName}\">Will be thrown if the corresponding composition root was not specified or if the root requires root arguments. To specify a composition root use API method such as <see cref=\"{Names.IConfigurationTypeName}.Root{{T}}\"/>.</exception>");
@@ -47,6 +48,13 @@ public void AddComments(CompositionCode composition, Root root)
47
48
code.AppendLine("/// </para>");
48
49
}
49
50
51
+
if(root.RootArgs.Length>0)
52
+
{
53
+
code.AppendLine("/// <para>");
54
+
code.AppendLine($"/// This root requires root arguments and is generated as a method. It cannot be resolved by generated {hints.ResolveMethodName}/{hints.ResolveByTagMethodName} methods.");
0 commit comments