-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
171 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
The recommended Microsoft approach is to use interfaces. You can register something to the collection with the following syntax: | ||
|
||
`collection.AddSingleton<TInterface, TImplementation>();` | ||
|
||
The advantage of this is that you can create two implementation for the same interface. We can see an example in a game where there needed to be a [WinForms](https://learn.microsoft.com/en-us/dotnet/desktop/winforms/overview/?view=netdesktop-7.0) and a [WPF](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/overview/?view=netdesktop-7.0) project. These frameworks are not quite compatible with each other. | ||
|
||
There is a [ITileFactory](https://github.com/joshika39/dotnet-bomber-game/blob/main/Bomber.BL/Tiles/Factories/ITileFactory.cs) which has two different implementation one for each framework: | ||
- [FormsTileFactory](https://github.com/joshika39/dotnet-bomber-game/blob/main/Bomber.UI.Forms/Tiles/Factories/FormsTileFactory.cs) | ||
- [WpfTileFactory](https://github.com/joshika39/dotnet-bomber-game/blob/main/Bomber.UI.WPF/Tiles/Factories/WpfTileFactory.cs) | ||
|
||
These are separately registered for their specific project, and this way, the core backend logic can reference the interface not caring about which is the implementation behind it. | ||
|
||
### Example | ||
|
||
```cs | ||
public interface IExampleSerice | ||
|
||
internal class ExampleService : IExampleSerice | ||
{ | ||
public ExampleService(IServiceA servicA, ISmallService smallService, IBigService bigService) | ||
{ | ||
// Do the assignments | ||
} | ||
} | ||
``` | ||
|
||
Registering and using it: | ||
|
||
```cs | ||
var collection = new ServiceCollection(); | ||
collection.AddSingleton<IExampleService, ExampleService>() | ||
var provider = collection.BuildServiceProvider(); | ||
var exService = provider.GetRequiredService<IExampleService>(); | ||
``` | ||
|
||
### Real life example | ||
|
||
We also follow this style in the [module of the project](https://github.com/joshika39/cs-tools/blob/9a09d0442ba76e75eda543a26aab81d5ded3e7ec/_src/Implementation/Module/CoreModule.cs#L25-L31): | ||
|
||
```cs | ||
collection.AddScoped<ILogger, Logger.Logger>(_ => new Logger.Logger(Guid.NewGuid())); | ||
collection.AddScoped<IWriter, Writer>(); | ||
collection.AddScoped<IReader, Reader>(); | ||
collection.AddScoped<IDataParser, DefaultDataParser>() | ||
``` | ||
|
||
## References | ||
- [Dependency injection guidelines - .NET | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines) | ||
- [Dependency injection in ASP.NET Core | Microsoft Learn](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
There are two main ways to use the package. | ||
- Registering your classes in the same collection that you need for registering this module. (Recommended) | ||
- Passing the `IServiceProvider` through the constructor. | ||
|
||
## Registering your own classes | ||
|
||
The advantage of using a Container is that let's say you have a class of which constructor takes 3 parameters: | ||
|
||
```cs | ||
internal class ExampleService | ||
{ | ||
public ExampleService(ServiceA servicA, SmallService smallService, BigService bigService) | ||
{ | ||
// Do the assignments | ||
} | ||
} | ||
``` | ||
|
||
> **NOTE**: It is recommended to use [interfaces](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface) and then [register and resolve](https://github.com/joshika39/cs-tools/wiki/Register-and-resolve-with-inteface) them with the interface | ||
Now if you register all of your services in the collection and register the `ExampleService` as well, then you can just call. | ||
|
||
```cs | ||
var collection = new ServiceCollection(); | ||
// The registrations shoud go here | ||
var provider = collection.BuildServiceProvider(); | ||
var exService = provider.GetRequiredService<ExampleService>(); | ||
``` | ||
|
||
And everything will be injected for you. For example the [`Reader`](https://github.com/joshika39/cs-tools/blob/9a09d0442ba76e75eda543a26aab81d5ded3e7ec/_src/Implementation/IO/Reader.cs#L17-L22) from this package: | ||
|
||
```cs | ||
public Reader(ILogger logger, IWriter writer, IDataParser dataParser) | ||
{ | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
_writer = writer ?? throw new ArgumentNullException(nameof(writer)); | ||
_dataParser = dataParser ?? throw new ArgumentNullException(nameof(dataParser)); | ||
} | ||
``` | ||
The there parameters are registered in the [module of the project](https://github.com/joshika39/cs-tools/blob/9a09d0442ba76e75eda543a26aab81d5ded3e7ec/_src/Implementation/Module/CoreModule.cs#L25-L31): | ||
|
||
```cs | ||
collection.AddScoped<ILogger, Logger.Logger>(_ => new Logger.Logger(Guid.NewGuid())); | ||
collection.AddScoped<IWriter, Writer>(); | ||
collection.AddScoped<IReader, Reader>(); | ||
collection.AddScoped<IDataParser, DefaultDataParser>() | ||
``` | ||
|
||
### Registering in a separate class | ||
You can create a "Module" for your project by creating a new class and implementing the `IModule` from the `joshika39.Core`. | ||
|
||
Let's say you have a `PdfService` and a `MyLogger` class that you want to register. First you need to create a *module* class: | ||
|
||
```cs | ||
internal class MyModule : IModule | ||
{ | ||
public void LoadModules(IServiceCollection collection) | ||
{ | ||
// Do your class registrations | ||
// Read about lifecycles here: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines | ||
collection.AddSingleton<PdfService>(); | ||
collection.AddScoped<MyLogger>(); | ||
} | ||
} | ||
``` | ||
|
||
Then you just have to call your module's `LoadModules` function: | ||
|
||
```cs | ||
var collection = new ServiceCollection(); | ||
new CoreModule().LoadModules(collection, "reader-tests"); | ||
new MyModule().LoadModules(collection); | ||
var provider = collection.BuildServiceProvider(); | ||
``` | ||
### Registering directly in the composition root | ||
This is a the same if you take out the *core* of your `LoadModules` function from to [previous example](#Registering-in-a-separate-class) | ||
|
||
```cs | ||
var collection = new ServiceCollection(); | ||
new CoreModule().LoadModules(collection, "reader-tests"); | ||
|
||
// Do your class registrations | ||
// Read about lifecycles here: https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines | ||
collection.AddSingleton<PdfService>(); | ||
collection.AddScoped<MyLogger>(); | ||
|
||
var provider = collection.BuildServiceProvider(); | ||
``` | ||
|
||
## `IServiceProvider` in the constructor | ||
Basically you only need to create you class and pass the `IServiceProvider` and then manually resolve the services | ||
|
||
```cs | ||
internal class ExampleService | ||
{ | ||
private readonly ServiceA _serviceA; | ||
private readonly SmallService _smallService; | ||
private readonly BigService _bigService; | ||
|
||
public ExampleService(IServiceProvider serviceProvider) | ||
{ | ||
_serviceA = serviceProvider.GetRequiredService<ServiceA>(); | ||
_smallService = serviceProvider.GetRequiredService<SmallService>(); | ||
_bigService = serviceProvider.GetRequiredService<BigService>(); | ||
} | ||
} | ||
``` | ||
|
||
And this is how to create the object: | ||
|
||
```cs | ||
var collection = new ServiceCollection(); | ||
new CoreModule().LoadModules(collection, "reader-tests"); | ||
var provider = collection.BuildServiceProvider(); | ||
|
||
var exampleService = new ExampleService(provider); | ||
``` | ||
## References | ||
- [Dependency injection guidelines - .NET | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines) | ||
- [Dependency injection in ASP.NET Core | Microsoft Learn](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0) |