Home > other >  Dependency Injection on .NET: how DI framework can return objects based on conditions
Dependency Injection on .NET: how DI framework can return objects based on conditions

Time:01-11

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>>();
  •  Tags:  
  • Related