Rafael
Rafael

Reputation: 1655

Simple Injector: Implementation that depends on http request

I'm a beginner with Simple Injector and have a scenario where I need help to implement. I will try to simplify what I need to do.

I have a WebAPI where I need to authenticate users and based on the type of user choose an implementation.

Consider this structure

public interface ICarRepository {
    void SaveCar(Car car);
}

//Some implementation for ICarRepository

public interface ICarLogic {
    void CreateCar(Car car);
}

public class CarLogicStandard: ICarLogic {
    private ICarRepository _carRepository;

    public CarLogicStandard(ICarRepository carRepository) {
        _carRepository = carRepository;
    }

    public void CreateCar(Car car) {
        car.Color = "Blue";
        _carRepository.SaveCar();
        //Other stuff...
    }
}

public class CarLogicPremium: ICarLogic {
    private ICarRepository _carRepository;

    public CarLogicPremium(ICarRepository carRepository) {
        _carRepository = carRepository;
    }

    public void CreateCar(Car car) {
        car.Color = "Red";
        _carRepository.SaveCar();
        //Other stuff 2...
    }

}

And now I have a controller

public class CarController: ApiController {
    private ICarLogic _carLogic;
    public CarController(ICarLogic carLogic) {
        _carLogic = carLogic;
    }

    public void Post(somePostData) {
        //Identify the user based on post data
        //....

        Car car = somePostData.SomeCar();
        _carLogic.CreateCar(car);
    }


}

The code above will not work because in my request I need to identify the user. If it is a premium user the controller should use the CarLogicPremium and if it is a standard user the controller should use the CarLogicStandard.

I can configure the repository and others interfaces that don't need this logic on Global.asax however, since this case I need the request to decide which implementation should be used, I supose that I need to solve this in some other way.

There is a "Simple Injector" way to handle this? Or should I try another approach?

Upvotes: 3

Views: 501

Answers (1)

qujck
qujck

Reputation: 14578

The simplest solution would be to configure the decision in the composition root, along with the rest of the container's configuration:

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);

    var container = new Container();

    container.Register<CarLogicStandard>();
    container.Register<CarLogicPremium>();
    container.RegisterPerWebRequest<ICarRepository, CarRepository>();
    container.Register<ICarLogic>(
        () =>
            HttpContext.Current != null &&
                HttpContext.Current.User != null &&
                HttpContext.Current.User.IsInRole("Premium")
            ? (ICarLogic)container.GetInstance<CarLogicPremium>()
            : (ICarLogic)container.GetInstance<CarLogicStandard>()
        );

    // This is an extension method from the integration package.
    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    container.Verify();

    GlobalConfiguration.Configuration.DependencyResolver =
        new SimpleInjectorWebApiDependencyResolver(container);
}

You could also create an abstraction over the current user and decorate standard features with premium features

public class CarLogicPremium : ICarLogic
{
    private readonly ICarLogic decorated;
    private readonly ICurrentUser currentUser;
    private readonly ICarRepository carRepository;

    public CarLogicPremium(
        ICarLogic decorated,
        ICurrentUser currentUser,
        ICarRepository carRepository)
    {
        this.decorated = decorated;
        this.currentUser = currentUser;
        this.carRepository = carRepository;
    }

    public void CreateCar(Car car)
    {
        if (currentUser.IsPremiumMember)
        {
            car.Color = "Red";
            this.carRepository.SaveCar(car);
            //Other stuff 2...
        }
        else
        {
            this.decorated.CreateCar(car);
        }
    }
}

which would be configured a bit like this

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);

    var container = new Container();

    container.Register<ICurrentUser, HttpCurrentUserProxy>();
    container.RegisterPerWebRequest<ICarRepository, CarRepository>();
    container.Register<ICarLogic, CarLogicStandard>();
    container.RegisterDecorator(typeof(ICarLogic), typeof(CarLogicPremium));

    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    container.Verify();

    GlobalConfiguration.Configuration.DependencyResolver =
        new SimpleInjectorWebApiDependencyResolver(container);
}

But it really depends how many variations of services you will be creating over time. If you will be constantly adding new premium features you should look to implement a variation of the Try-X pattern. Let me know if one of the above works for you or if you need more info ...

Upvotes: 2

Related Questions