Skip to content

Commit

Permalink
docs fix
Browse files Browse the repository at this point in the history
  • Loading branch information
joshika39 committed Nov 5, 2023
1 parent 9a09d04 commit 9438459
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/Home.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ var provider = collection.BuildServiceProvider();

## Next steps

Follow up: **[Using the package]()**
Follow up: **[Using the package](https://github.com/joshika39/cs-tools/wiki/Using-the-package)**
## References
- [Dependency injection guidelines - .NET | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines)
49 changes: 49 additions & 0 deletions docs/Register and resolve with inteface.md
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)
121 changes: 121 additions & 0 deletions docs/Using the package.md
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)

0 comments on commit 9438459

Please sign in to comment.