Reputation: 6684
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
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
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
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
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