tucaz
tucaz

Reputation: 6684

Resolving automatic and manual dependencies

I´m having a little bit of trouble sorting a way to manage automatic resolved and manual dependencies in my classes.

Let´s say I have two classes to calculate prices: one calculates how much I will charge for shipping and the other how much I will charge for the entire order. The second uses the first in order to sum the shipping price to the entire order price.

Both classes have a dependency to a third class that I will call ExchangeRate which gives me the exchange rate I should use for price calculation.

So far we have this chain of dependency:

OrderCalculator -> ShippingCalculator -> ExchangeRate

I´m using Ninject to resolve these dependencies and this was working until now. Now I have a requirement that the rate returned by ExchangeRate class will vary upon a parameter that will be provided in the Constructor (because the object won´t work without this, so to make the dependency explicit it´s placed on the constructor) coming from a user input. Because of that I can no longer resolve my dependencies automatically.

Whenever I want to the OrderCalculator or any other classes that depends on ExchangeRate I cannot ask the Ninject container to resolve it to me since I need to provide the parameter in the constructor.

What do u suggest in this case?

Thanks!

EDIT: Let's add some code

This chain of objects is consumed by a WCF service and I'm using Ninject as the DI container.

public class OrderCalculator : IOrderCalculator
{ 
        private IExchangeRate _exchangeRate; 
        public OrderCalculator(IExchangeRate exchangeRate) 
        { 
                _exchangeRate = exchangeRate; 
        } 
        public decimal CalculateOrderTotal(Order newOrder) 
        { 
                var total = 0m; 
                foreach(var item in newOrder.Items) 
                { 
                        total += item.Price * _exchangeRate.GetRate();
                } 
                return total;               
        } 
} 

public class ExchangeRate : IExchangeRate 
{ 
        private RunTimeClass _runtimeValue; 
        public ExchangeRate(RunTimeClass runtimeValue) 
        { 
                _runtimeValue = runtimeValue; 
        } 
        public decimal GetRate() 
        { 
                //returns the rate according to _runtimeValue
                if(_runtimeValue == 1) 
                        return 15.3m; 
                else if(_runtimeValue == 2) 
                        return 9.9m 
                else 
                        return 30m; 
        } 
} 

//WCF Service
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue)
{   
    //I would like to pass the runtimeValue when resolving the IOrderCalculator depedency using a dictionary or something
    //Something like this ObjectFactory.Resolve(runtimeValue);
    IOrderCalculator calculator = ObjectFactory.Resolve();    
    return calculator.CalculateOrderTotal(newOrder);    
}

Upvotes: 5

Views: 1573

Answers (5)

tucaz
tucaz

Reputation: 6684

I ended up doing something totally different.

Before I call the ObjectFactory to resolve the dependencies for me I create a new instance of the IExchangeRate using the runTimeValue and tell the IoC/DI container to use it instead of creating a new one. This way the whole chain of objects is preserved and there is no need for factories.


//WCF Service
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue)
{   
    IExchangeRate ex = new ExchangeRate(runtimeValue);
    IOrderCalculator calculator = ObjectFactory.With<IExchangeRate>(ex).GetInstance();
    return calculator.CalculateOrderTotal(newOrder);    
}

But since Ninject doesn't have a way to do this (only Rebind which is not what I want) I changed my container to StructureMap.

Thanks guys for all your help! Really appreciate it!

Upvotes: 1

WW.
WW.

Reputation: 24311

Updated based on the code:

//WCF Service
public decimal GetTotalForOrder(Order newOrder, RunTimeClass runtimeValue)
{   
    //I would like to pass the runtimeValue when resolving the IOrderCalculator depedency using a dictionary or something
    //Something like this ObjectFactory.Resolve(runtimeValue);
    IOrderCalculator calculator = ObjectFactory.Resolve();    
    return calculator.CalculateOrderTotal(newOrder);    
}

Notice how this method only wants runtimeValue so that it can pass it to something else? This is happening because object construction and runtime responsibilities are mixed. I think you should be asking for a IOrderCalculator in the constructor for this method.

So this method just becomes:

//WCF Service
public decimal GetTotalForOrder(Order newOrder, IOrderCalculator calculator)
{   
    return calculator.CalculateOrderTotal(newOrder);    
}

Now, when you construct your IOrderCalculator you should pass runtimeValue to it's constructor. It's a bit hard to answer when we do not know what runtimeValue is or where it comes from.

Upvotes: 0

Ruben Bartelink
Ruben Bartelink

Reputation: 61893

You may find injecting factory delegates or providers to be useful. Both are pretty much impls of Mark's (+1d) answer.

Upvotes: 1

Mark Seemann
Mark Seemann

Reputation: 233377

As always, when you have a partial dependency on a run-time value, the solution is an Abstract Factory.

Something like this should work:

public interface IExchangeRateFactory
{
    ExchangeRate GetExchangeRate(object runTimeValue);
}

Now inject an IExchangeRateFactory into your consumers instead of ExchangeRate and use the GetExchangeRate method to convert the run-time value to an ExchangeRate instance.

Obviously you will also need to provide an implementation of IExchangeRateFactory and configure NInject to map the interface to your implementation.

Upvotes: 7

Steven Sudit
Steven Sudit

Reputation: 19630

Move that initialization out of the constructor.

Upvotes: 0

Related Questions