Ryan Alford
Ryan Alford

Reputation: 7594

TinyIoC - Multiple Implementations of Interface

I am just beginning to learn about IoC and Dependency Injection. I am planning on doing a MonoTouch project and wanted to use TinyIoC but I wanted to test it out first. I'm creating a dummy credit card processing console app and I'm having trouble with how to configure TinyIoC since I have multiple implementations of my interface. This is my test app.

Interface:

public interface IPaymentProcessor
{
    void ProcessPayment(string cardNumber);
}

Two Implementations of the interface:

VisaPaymentProcessor

public class VisaPaymentProcessor : IPaymentProcessor
{
    public void ProcessPayment(string cardNumber)
    {
        if (cardNumber.Length != 13 && cardNumber.Length != 16)
        {
            new ArgumentException("Card Number isn't the correct length");
        }

        // some code for processing payment
    }
}

AmexPaymentProcessor

public class AmexPaymentProcessor : IPaymentProcessor
{
    public void ProcessPayment(string cardNumber)
    {
        if (cardNumber.Length != 15)
        {
            new ArgumentException("Card Number isn't the correct length");
        }

        // some code for processing the payment
    }
}

Simple stuff. Now I have a class that accepts the interface as a parameter in the constructor....

CreditCardProcessor

public class CreditCardProcessor
{
    public IPaymentProcessor PaymentProcessor { get; set; }

    public CreditCardProcessor(IPaymentProcessor processor)
    {
        this.PaymentProcessor = processor;
    }

    public void ProcessPayment(string creditCardNumber)
    {
        this.PaymentProcessor.ProcessPayment(creditCardNumber);
    }
}

My console app looks like this....

class Program
{
    static void Main(string[] args)
    {
        TinyIoCContainer.Current.AutoRegister();

        var creditCardProcessor = TinyIoCContainer.Current.Resolve<CreditCardProcessor>();
        creditCardProcessor.ProcessPayment("1234567890123456"); // 16 digits
    }
}

So I am trying to figure out how to tell the Resolve which implementation of the interface to pass to the constructor. If I run this code, I will always use the VisaPaymentProcessor implementation.

So how can I make TinyIoC pass the AmexPaymentProcessor implementation to the constructor rather than the VisaPaymentProcessor(which seems to be the default)?

Upvotes: 8

Views: 4578

Answers (3)

Kristoffer Schroeder
Kristoffer Schroeder

Reputation: 688

Something like this in Global.asax or application entry (Modified for your example)

        const string nameTrim = "paymentprocessor"; 
        var type = typeof(IPaymentProcessor);
        AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(s => s.GetTypes())
            .Where(x => type.IsAssignableFrom(x) && x.IsClass).ToList()
            .ForEach(t =>
            {
                var name = t.Name.ToLower();
                if (name.EndsWith(nameTrim))
                    name = name.Substring(0, name.Length - nameTrim.Length);

                TinyIoCContainer.Current.Register(type, t, name);
            });

It finds alla implementations of IPaymentProcessor and registers them with classname (-PaymentProcessor, if the classname ends with PaymentProcessor)

Then I can resolve for example "AmexPaymentProcessor" with

        IPaymentProcessor handler;
        if (TinyIoCContainer.Current.TryResolve("amex", out handler))
        {
            response = handler.ProcessPayment(cardNumber);
        }

Upvotes: 2

Steven Robbins
Steven Robbins

Reputation: 26599

I'm not really sure what you're trying to achieve here, but if you have multiple implementations of an interface and you want a specific one then you need to register each one with a name, or use RegisterMultiple, which uses the type name for a name, then resolve using that name and use that along with NamedParameterOverloads to specify which one you want.

It sounds more like though that you might want some kind of ProcessorFactory, or a facade of some kind, that takes a dependency on IEnumerable and supplies/acts as a facade for the correct implementation depending on the number passed in.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1504052

I haven't used TinyIoC myself, but I suspect you want:

TinyIoCContainer.Current.Register(typeof(IPaymentProcessor),
                                  typeof(AmexPaymentProcessor));

(If you want to use Amex.)

There are various other Register overloads available, including one which takes a name to use, which may be useful when you resolve. It really depends on what you're trying to achieve, which isn't terribly clear from the question.

Upvotes: 7

Related Questions