diff --git a/README.md b/README.md index b0d8a131..4aa2f859 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,7 @@ dotnet run - [Generic builders](readme/generic-builders.md) ### Attributes - [Constructor ordinal attribute](readme/constructor-ordinal-attribute.md) +- [Dependency attribute](readme/dependency-attribute.md) - [Member ordinal attribute](readme/member-ordinal-attribute.md) - [Tag attribute](readme/tag-attribute.md) - [Type attribute](readme/type-attribute.md) diff --git a/readme/build-up-of-an-existing-generic-object.md b/readme/build-up-of-an-existing-generic-object.md index e78a4d05..3a4ca029 100644 --- a/readme/build-up-of-an-existing-generic-object.md +++ b/readme/build-up-of-an-existing-generic-object.md @@ -39,14 +39,14 @@ interface IDependency class Dependency : IDependency where T: struct { - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection + [Dependency] public string Name { get; set; } = ""; public T Id { get; private set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(0)] + // The Dependency attribute specifies to perform an injection + [Dependency] public void SetId(T id) => Id = id; } @@ -111,8 +111,8 @@ partial class Composition Guid transientGuid2 = Guid.NewGuid(); Dependency transientDependency1; Dependency localDependency56 = new Dependency(); - localDependency56.SetId(transientGuid2); localDependency56.Name = name; + localDependency56.SetId(transientGuid2); transientDependency1 = localDependency56; return new Service(transientDependency1); } diff --git a/readme/build-up-of-an-existing-object.md b/readme/build-up-of-an-existing-object.md index 0bea9732..cbeb7f03 100644 --- a/readme/build-up-of-an-existing-object.md +++ b/readme/build-up-of-an-existing-object.md @@ -37,14 +37,14 @@ interface IDependency class Dependency : IDependency { - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection and its order + [Dependency] public string Name { get; set; } = ""; public Guid Id { get; private set; } = Guid.Empty; - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(0)] + // The Dependency attribute specifies to perform an injection and its order + [Dependency] public void SetId(Guid id) => Id = id; } @@ -107,8 +107,8 @@ partial class Composition Guid transientGuid2 = Guid.NewGuid(); Dependency transientDependency1; var localDependency50 = new Dependency(); - localDependency50.SetId(transientGuid2); localDependency50.Name = name; + localDependency50.SetId(transientGuid2); transientDependency1 = localDependency50; return new Service(transientDependency1); } diff --git a/readme/builders.md b/readme/builders.md index 7e0fe1a6..018e23ea 100644 --- a/readme/builders.md +++ b/readme/builders.md @@ -44,11 +44,12 @@ record Service1: IService { public Guid Id { get; private set; } = Guid.Empty; - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection + [Dependency] public IDependency? Dependency { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(2)] + // The Dependency attribute specifies to perform an injection + [Dependency] public void SetId(Guid id) => Id = id; } @@ -56,14 +57,13 @@ record Service2: IService { public Guid Id { get; private set; } = Guid.Empty; - [Ordinal(1)] + [Dependency] public IDependency? Dependency => DependencyFactory?.Invoke(); - [Ordinal(2)] + [Dependency] public Func? DependencyFactory { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(3)] + [Dependency] public void SetId(Guid id) => Id = id; } ``` @@ -200,10 +200,10 @@ classDiagram Dependency --|> IDependency Composition ..> Service2 : Service2 BuildUp(Pure.DI.UsageTests.Basics.BuilderScenario.Service2 instance) Composition ..> Service1 : Service1 BuildUp(Pure.DI.UsageTests.Basics.BuilderScenario.Service1 instance) - Service2 o-- Service2 : "2M01D24di" Argument "instance" + Service2 o-- Service2 : "0M01D24di" Argument "instance" Service2 o-- "PerBlock" FuncᐸIDependencyᐳ : FuncᐸIDependencyᐳ Service2 *-- Guid : Guid - Service1 o-- Service1 : "0M01D24di" Argument "instance" + Service1 o-- Service1 : "2M01D24di" Argument "instance" Service1 *-- Dependency : IDependency Service1 *-- Guid : Guid FuncᐸIDependencyᐳ *-- Dependency : IDependency diff --git a/readme/complex-generic-root-arguments.md b/readme/complex-generic-root-arguments.md index 531eb621..fe7fbf0b 100644 --- a/readme/complex-generic-root-arguments.md +++ b/readme/complex-generic-root-arguments.md @@ -26,10 +26,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(MyData data) => Val = data.Value; diff --git a/readme/dependency-attribute.md b/readme/dependency-attribute.md new file mode 100644 index 00000000..c545f879 --- /dev/null +++ b/readme/dependency-attribute.md @@ -0,0 +1,181 @@ +#### Dependency attribute + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Attributes/DependencyAttributeScenario.cs) + +When applied to a property or field, these type members will also participate in dependency injection in the appropriate order from smallest value to largest. + + +```c# +using Shouldly; +using Pure.DI; +using System.Text; + +DI.Setup(nameof(PersonComposition)) + .Arg("personId") + .Arg("personName") + .Arg("personBirthday") + .Bind().To() + + // Composition root + .Root("Person"); + +var composition = new PersonComposition( + personId: 123, + personName: "Nik", + personBirthday: new DateTime(1977, 11, 16)); + +var person = composition.Person; +person.Name.ShouldBe("123 Nik 1977-11-16"); + +interface IPerson +{ + string Name { get; } +} + +class Person : IPerson +{ + private readonly StringBuilder _name = new(); + + public string Name => _name.ToString(); + + [Dependency] public int Id; + + // The Ordinal attribute specifies to perform an injection, + // the integer value in the argument specifies + // the ordinal of injection + [Dependency(ordinal: 1)] + public string FirstName + { + set + { + _name.Append(Id); + _name.Append(' '); + _name.Append(value); + } + } + + [Dependency(ordinal: 2)] + public DateTime Birthday + { + set + { + _name.Append(' '); + _name.Append($"{value:yyyy-MM-dd}"); + } + } +} +``` + +
+Running this code sample locally + +- Make sure you have the [.NET SDK 9.0](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) or later is installed +```bash +dotnet --list-sdk +``` +- Create a net9.0 (or later) console application +```bash +dotnet new console -n Sample +``` +- Add references to NuGet packages + - [Pure.DI](https://www.nuget.org/packages/Pure.DI) + - [Shouldly](https://www.nuget.org/packages/Shouldly) +```bash +dotnet add package Pure.DI +dotnet add package Shouldly +``` +- Copy the example code into the _Program.cs_ file + +You are ready to run the example 🚀 +```bash +dotnet run +``` + +
+ +The attribute `Dependency` is part of the API, but you can use your own attribute at any time, and this allows you to define them in the assembly and namespace you want. + +The following partial class will be generated: + +```c# +partial class PersonComposition +{ + private readonly PersonComposition _root; + + private readonly int _argPersonId; + private readonly string _argPersonName; + private readonly DateTime _argPersonBirthday; + + [OrdinalAttribute(128)] + public PersonComposition(int personId, string personName, DateTime personBirthday) + { + _argPersonId = personId; + _argPersonName = personName ?? throw new ArgumentNullException(nameof(personName)); + _argPersonBirthday = personBirthday; + _root = this; + } + + internal PersonComposition(PersonComposition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + _argPersonId = _root._argPersonId; + _argPersonName = _root._argPersonName; + _argPersonBirthday = _root._argPersonBirthday; + } + + public IPerson Person + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Person transientPerson0 = new Person(); + transientPerson0.Id = _argPersonId; + transientPerson0.FirstName = _argPersonName; + transientPerson0.Birthday = _argPersonBirthday; + return transientPerson0; + } + } +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Person --|> IPerson + PersonComposition ..> Person : IPerson Person + Person o-- Int32 : Argument "personId" + Person o-- String : Argument "personName" + Person o-- DateTime : Argument "personBirthday" + namespace Pure.DI.UsageTests.Attributes.DependencyAttributeScenario { + class IPerson { + <> + } + class Person { + +Person() + +Int32 Id + +String FirstName + +DateTime Birthday + } + class PersonComposition { + <> + +IPerson Person + } + } + namespace System { + class DateTime { + <> + } + class Int32 { + <> + } + class String { + } + } +``` + diff --git a/readme/field-injection.md b/readme/field-injection.md index 5641e95e..23478826 100644 --- a/readme/field-injection.md +++ b/readme/field-injection.md @@ -31,10 +31,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] internal IDependency? DependencyVal; + [Dependency] internal IDependency? DependencyVal; public IDependency? Dependency => DependencyVal; } diff --git a/readme/generic-builders.md b/readme/generic-builders.md index 16b256a0..de4f0262 100644 --- a/readme/generic-builders.md +++ b/readme/generic-builders.md @@ -34,11 +34,10 @@ record Service: IService { public T Id { get; private set; } = default(T); - [Ordinal(1)] + [Dependency] public IDependency? Dependency { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(2)] + [Dependency] public void SetId([Tag(Tag.Id)] T id) => Id = id; } ``` diff --git a/readme/generic-root-arguments.md b/readme/generic-root-arguments.md index f716eff2..5d8c270f 100644 --- a/readme/generic-root-arguments.md +++ b/readme/generic-root-arguments.md @@ -23,10 +23,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(T dependency) => Dependency = dependency; diff --git a/readme/method-injection.md b/readme/method-injection.md index ada959fc..87738106 100644 --- a/readme/method-injection.md +++ b/readme/method-injection.md @@ -31,10 +31,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(IDependency dependency) => Dependency = dependency; diff --git a/readme/property-injection.md b/readme/property-injection.md index f6f24536..6c4a79f6 100644 --- a/readme/property-injection.md +++ b/readme/property-injection.md @@ -31,10 +31,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public IDependency? Dependency { get; set; } } ``` diff --git a/readme/tag-on-a-method-argument.md b/readme/tag-on-a-method-argument.md index de684979..bce3f7a0 100644 --- a/readme/tag-on-a-method-argument.md +++ b/readme/tag-on-a-method-argument.md @@ -35,7 +35,7 @@ interface IService class Service : IService { - [Ordinal(1)] + [Dependency] public void Initialize(IDependency dep) => Dependency = dep; diff --git a/tests/Pure.DI.UsageTests/Advanced/TagOnMethodArgScenario.cs b/tests/Pure.DI.UsageTests/Advanced/TagOnMethodArgScenario.cs index 19fdecb7..89967b24 100644 --- a/tests/Pure.DI.UsageTests/Advanced/TagOnMethodArgScenario.cs +++ b/tests/Pure.DI.UsageTests/Advanced/TagOnMethodArgScenario.cs @@ -65,7 +65,7 @@ interface IService class Service : IService { - [Ordinal(1)] + [Dependency] public void Initialize(IDependency dep) => Dependency = dep; diff --git a/tests/Pure.DI.UsageTests/Attributes/DependencyAttributeScenario.cs b/tests/Pure.DI.UsageTests/Attributes/DependencyAttributeScenario.cs new file mode 100644 index 00000000..413986f0 --- /dev/null +++ b/tests/Pure.DI.UsageTests/Attributes/DependencyAttributeScenario.cs @@ -0,0 +1,92 @@ +/* +$v=true +$p=1 +$d=Dependency attribute +$h=When applied to a property or field, these type members will also participate in dependency injection in the appropriate order from smallest value to largest. +$f=The attribute `Dependency` is part of the API, but you can use your own attribute at any time, and this allows you to define them in the assembly and namespace you want. +$r=Shouldly +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedParameter.Local +// ReSharper disable ArrangeTypeModifiers + +namespace Pure.DI.UsageTests.Attributes.DependencyAttributeScenario; + +using System.Text; +using Shouldly; +using Xunit; + +// { +//# using Pure.DI; +//# using System.Text; +// } + +public class Scenario +{ + [Fact] + public void Run() + { + // Resolve = Off +// { + DI.Setup(nameof(PersonComposition)) + .Arg("personId") + .Arg("personName") + .Arg("personBirthday") + .Bind().To() + + // Composition root + .Root("Person"); + + var composition = new PersonComposition( + personId: 123, + personName: "Nik", + personBirthday: new DateTime(1977, 11, 16)); + + var person = composition.Person; + person.Name.ShouldBe("123 Nik 1977-11-16"); +// } + composition.SaveClassDiagram(); + } +} + +// { +interface IPerson +{ + string Name { get; } +} + +class Person : IPerson +{ + private readonly StringBuilder _name = new(); + + public string Name => _name.ToString(); + + [Dependency] public int Id; + + // The Ordinal attribute specifies to perform an injection, + // the integer value in the argument specifies + // the ordinal of injection + [Dependency(ordinal: 1)] + public string FirstName + { + set + { + _name.Append(Id); + _name.Append(' '); + _name.Append(value); + } + } + + [Dependency(ordinal: 2)] + public DateTime Birthday + { + set + { + _name.Append(' '); + _name.Append($"{value:yyyy-MM-dd}"); + } + } +} +// } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Basics/BuildUpScenario.cs b/tests/Pure.DI.UsageTests/Basics/BuildUpScenario.cs index 99b3457c..53b60096 100644 --- a/tests/Pure.DI.UsageTests/Basics/BuildUpScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/BuildUpScenario.cs @@ -60,14 +60,14 @@ interface IDependency class Dependency : IDependency { - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection and its order + [Dependency] public string Name { get; set; } = ""; public Guid Id { get; private set; } = Guid.Empty; - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(0)] + // The Dependency attribute specifies to perform an injection and its order + [Dependency] public void SetId(Guid id) => Id = id; } diff --git a/tests/Pure.DI.UsageTests/Basics/BuilderScenario.cs b/tests/Pure.DI.UsageTests/Basics/BuilderScenario.cs index c087179b..33dcb8c3 100644 --- a/tests/Pure.DI.UsageTests/Basics/BuilderScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/BuilderScenario.cs @@ -66,11 +66,12 @@ record Service1: IService { public Guid Id { get; private set; } = Guid.Empty; - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection + [Dependency] public IDependency? Dependency { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(2)] + // The Dependency attribute specifies to perform an injection + [Dependency] public void SetId(Guid id) => Id = id; } @@ -78,14 +79,13 @@ record Service2: IService { public Guid Id { get; private set; } = Guid.Empty; - [Ordinal(1)] + [Dependency] public IDependency? Dependency => DependencyFactory?.Invoke(); - [Ordinal(2)] + [Dependency] public Func? DependencyFactory { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(3)] + [Dependency] public void SetId(Guid id) => Id = id; } // } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Basics/FieldInjectionScenario.cs b/tests/Pure.DI.UsageTests/Basics/FieldInjectionScenario.cs index d0f896e9..f9290f18 100644 --- a/tests/Pure.DI.UsageTests/Basics/FieldInjectionScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/FieldInjectionScenario.cs @@ -54,10 +54,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] internal IDependency? DependencyVal; + [Dependency] internal IDependency? DependencyVal; public IDependency? Dependency => DependencyVal; } diff --git a/tests/Pure.DI.UsageTests/Basics/MethodInjectionScenario.cs b/tests/Pure.DI.UsageTests/Basics/MethodInjectionScenario.cs index 60e47c70..a493c54a 100644 --- a/tests/Pure.DI.UsageTests/Basics/MethodInjectionScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/MethodInjectionScenario.cs @@ -54,10 +54,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(IDependency dependency) => Dependency = dependency; diff --git a/tests/Pure.DI.UsageTests/Basics/PropertyInjectionScenario.cs b/tests/Pure.DI.UsageTests/Basics/PropertyInjectionScenario.cs index c6889ed9..1327983f 100644 --- a/tests/Pure.DI.UsageTests/Basics/PropertyInjectionScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/PropertyInjectionScenario.cs @@ -54,10 +54,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public IDependency? Dependency { get; set; } } // } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Generics/ComplexGenericRootArgScenario.cs b/tests/Pure.DI.UsageTests/Generics/ComplexGenericRootArgScenario.cs index 642bc379..de2d63c9 100644 --- a/tests/Pure.DI.UsageTests/Generics/ComplexGenericRootArgScenario.cs +++ b/tests/Pure.DI.UsageTests/Generics/ComplexGenericRootArgScenario.cs @@ -54,10 +54,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(MyData data) => Val = data.Value; diff --git a/tests/Pure.DI.UsageTests/Generics/GenericBuildUpScenario.cs b/tests/Pure.DI.UsageTests/Generics/GenericBuildUpScenario.cs index 64e36e06..e3499122 100644 --- a/tests/Pure.DI.UsageTests/Generics/GenericBuildUpScenario.cs +++ b/tests/Pure.DI.UsageTests/Generics/GenericBuildUpScenario.cs @@ -62,14 +62,14 @@ interface IDependency class Dependency : IDependency where T: struct { - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(1)] + // The Dependency attribute specifies to perform an injection + [Dependency] public string Name { get; set; } = ""; public T Id { get; private set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(0)] + // The Dependency attribute specifies to perform an injection + [Dependency] public void SetId(T id) => Id = id; } diff --git a/tests/Pure.DI.UsageTests/Generics/GenericBuilderScenario.cs b/tests/Pure.DI.UsageTests/Generics/GenericBuilderScenario.cs index d3c8b79b..b4800b90 100644 --- a/tests/Pure.DI.UsageTests/Generics/GenericBuilderScenario.cs +++ b/tests/Pure.DI.UsageTests/Generics/GenericBuilderScenario.cs @@ -57,11 +57,10 @@ record Service: IService { public T Id { get; private set; } = default(T); - [Ordinal(1)] + [Dependency] public IDependency? Dependency { get; set; } - // The Ordinal attribute specifies to perform an injection and its order - [Ordinal(2)] + [Dependency] public void SetId([Tag(Tag.Id)] T id) => Id = id; } // } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Generics/GenericRootArgScenario.cs b/tests/Pure.DI.UsageTests/Generics/GenericRootArgScenario.cs index e3673948..d4209b76 100644 --- a/tests/Pure.DI.UsageTests/Generics/GenericRootArgScenario.cs +++ b/tests/Pure.DI.UsageTests/Generics/GenericRootArgScenario.cs @@ -50,10 +50,10 @@ interface IService class Service : IService { - // The Ordinal attribute specifies to perform an injection, + // The Dependency attribute specifies to perform an injection, // the integer value in the argument specifies // the ordinal of injection - [Ordinal(0)] + [Dependency] public void SetDependency(T dependency) => Dependency = dependency;