I am currently in the process of learning DI and experimenting with it. Here is my practice code and just an overview, what I am doing is like mimicking a driving simulator game where the player have two cars in their garage: Honda and Toyota. And the player uses Honda first, then changed to Toyota, and changed back to Honda.
using Microsoft.Extensions.DependencyInjection;
using System;
using Microsoft.Extensions.Hosting;
using static System.Guid;
namespace ConsoleApp2
{
// ----------- CARS
public interface ICar
{
string Description { get; }
}
public class Honda : ICar
{
private string Id = NewGuid().ToString()[^4..]; // just to see if instance has changed
public string Description => "HONDA " Id;
}
public class Toyota : ICar
{
private string Id = NewGuid().ToString()[^4..]; // just to see if instance has changed
public string Description => "TOYOTA " Id;
}
// ----------- CAR FACTORY
public enum CarType
{
Unknown,
Honda,
Toyota
}
public interface ICarFactory
{
CarType CarType { set; }
ICar GetCar();
}
public class CarFactory : ICarFactory
{
ICar _car;
CarType _carType = CarType.Unknown;
public CarType CarType
{
set
{
if(_carType != value)
{
switch (value)
{
case CarType.Honda: _car = new Honda(); break;
case CarType.Toyota: _car = new Toyota(); break;
default: throw new NotImplementedException();
}
_carType = value;
}
}
}
public ICar GetCar() { return _car; }
}
// ----------- MAIN
class Program
{
static void Main(string[] args)
{
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddSingleton<ICarFactory, CarFactory>())
.Build();
// --------------
ChooseCar(host.Services, CarType.Honda); // user chooses Honda
// lets say a user changed a configuration and wants to use Toyota all through
ChooseCar(host.Services, CarType.Toyota);
// lets say a user changed a configuration and wants to use the same Honda (previous selection)
ChooseCar(host.Services, CarType.Honda);
}
static void ChooseCar(IServiceProvider services, CarType c)
{
using IServiceScope serviceScope = services.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
var carfactory = provider.GetRequiredService<ICarFactory>();
carfactory.CarType = c;
Console.WriteLine("Using:" carfactory.GetCar().Description);
Console.WriteLine("Using:" carfactory.GetCar().Description);
}
}
}
Here is the output:
Using:HONDA 6573
Using:HONDA 6573
Using:TOYOTA c20f
Using:TOYOTA c20f
Using:HONDA b537
Using:HONDA b537
Based from this output, the returned Honda on the last part is a different instance from the first one, however, the player wants the previous Honda. Now, I could let the Factory handle/remember the instances to ensure the it will return the previous Honda. However, I think the Factory is doing more than it should be and I would like to know if there is something that the DI framework could help in this regards? If I proceed with that idea, the factory is doing most of the work on handling the previous instances of ICar. I would just like the factory to be responsible only on generating new cars, not remembering if a previous instance was made.
If there is anything that the DI could help eliminate the factory, that would be much better. What I am thinking right now, is to call the code below everytime a setting was changed and change the ICar to return either a Toyota or Honda but is it a bad practice to create a builder everytime and re-configure the services?
using IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices((_, services) =>
services.AddSingleton<ICar, Honda>()) // or Toyota, depending on what is needed
.Build();
Also, if I do it this way, how will I be handle cases where there are two or more Toyota's (or Honday's) in a garage (e.g. there is Toyota RAV-4 and Toyota Prius, maybe have different class for ToyotaPrius and ToyotaRav4)?
Basically, my end goal is for DI to return Honda or Toyota based from a user selection and if possible, without using a factory.
CodePudding user response:
I used to resolve multiple implementation issues using this approach:
public interface ICar
{
}
public class Honda : ICar { }
public class Toyota : ICar { }
public interface ICarProvider<TCar> where TCar : ICar
{
ICar ProvideCar();
}
public class CarProvider<TCar> : ICarProvider<TCar> where TCar : ICar, new()
{
private ICar _car;
public ICar ProvideCar()
{
if (_car == null)
{
_car = new TCar();
}
return _car;
}
}
And then in your startup, you need to have this:
services.AddSingleton<ICarProvider<Toyota>, CarProvider<Toyota>>();
services.AddSingleton<ICarProvider<Honda>, CarProvider<Honda>>();