19

I have this wcf method

Profile GetProfileInfo(string profileType, string profileName)

and a business rule:

if profileType is "A" read from database.

if profileType is "B" read from xml file.

The question is: how to implement it using a dependency injection container?

Marcin Szymczak
  • 11,199
  • 5
  • 55
  • 63
tartafe
  • 401
  • 5
  • 19

2 Answers2

24

Let's first assume that you have an IProfileRepository something like this:

public interface IProfileRepository
{
     Profile GetProfile(string profileName);
}

as well as two implementations: DatabaseProfileRepository and XmlProfileRepository. The issue is that you would like to pick the correct one based on the value of profileType.

You can do this by introducing this Abstract Factory:

public interface IProfileRepositoryFactory
{
    IProfileRepository Create(string profileType);
}

Assuming that the IProfileRepositoryFactory has been injected into the service implementation, you can now implement the GetProfileInfo method like this:

public Profile GetProfileInfo(string profileType, string profileName)
{
    return this.factory.Create(profileType).GetProfile(profileName);
}

A concrete implementation of IProfileRepositoryFactory might look like this:

public class ProfileRepositoryFactory : IProfileRepositoryFactory
{
    private readonly IProfileRepository aRepository;
    private readonly IProfileRepository bRepository;

    public ProfileRepositoryFactory(IProfileRepository aRepository,
        IProfileRepository bRepository)
    {
        if(aRepository == null)
        {
            throw new ArgumentNullException("aRepository");
        }
        if(bRepository == null)
        {
            throw new ArgumentNullException("bRepository");
        }

        this.aRepository = aRepository;
        this.bRepository = bRepository;
    }

    public IProfileRepository Create(string profileType)
    {
        if(profileType == "A")
        {
            return this.aRepository;
        }
        if(profileType == "B")
        {
            return this.bRepository;
        }

        // and so on...
    }
}

Now you just need to get your DI Container of choice to wire it all up for you...

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Those sequential `ifs` could be replaced be a faster/more legible `switch/case`. And `profileType` really should be an enumeration, not an arbitrary string. Other than that it's a great answer. :) – Aaronaught Jan 30 '10 at 18:23
  • 3
    Yes, no disagreement there, but I just went with the API given by the OP :) – Mark Seemann Jan 30 '10 at 18:32
  • In which way it can change if i don't know at compile time the number of repositories? And if my wcf have only dependencies with log library and these repositories, where is the best DI Container choice? is MEF a good choiche in this scenario? – tartafe Jan 31 '10 at 11:34
  • just a little clarification:i'll use the lo library for logging custom message in method and not for tracing – tartafe Jan 31 '10 at 11:41
  • You can configure most DI Containers using XML configuration, which is a good option when you don't know all the repositories at compile time, but MEF might also be a good option in this case. – Mark Seemann Jan 31 '10 at 11:55
  • In what package would you suggest placing the `ProfileRepositoryFactory` class? In the composition root (it concerns object creation), in the service layer (this is a business rule) or alongside the actual repository implementations? – MEMark Mar 29 '14 at 19:13
  • 1
    @MEMark That depends :) If it contains significant business logic, it should go in the Domain Model so that it can be properly tested, but that also means that it would have to be a [Manually Coded Factory](http://blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory); otherwise, you can put it in the Composition Root, in which case you have fewer implementation constraints. – Mark Seemann Mar 29 '14 at 19:23
  • Why don't you just add a profileType parameter to GetProfile ? – remi bourgarel Jan 19 '15 at 16:15
6

Great answer by Mark, However the solution given is not Abstract factory but the implementation of Standard Factory pattern. Please check how Marks classes fit in the Standard Factory Pattern UML diagram. Click here to see above classes applied to Factory pattern UML

Since in Factory pattern, the factory is aware of the concrete classes, we can make the code of the ProfileRepositoryFactory much simpler like below. The problem with injecting the different repositories to factory is that you have more code changes every time you add a new concrete type. With below code you only have to update the switch to include new concrete class


    public class ProfileRepositoryFactory : IProfileRepositoryFactory
    {
        public IProfileRepository Create(string profileType)
        {
            switch(profileType)
            {
                case "A":
                    return new DatabaseProfileRepository(); 

                case  "B":
                    return new XmlProfileRepository();
            }
        }
    }

Abstract Factory is more advanced pattern used for creating families of related or dependent objects without specifying their concrete classes. The UML class diagram available here explains it well.