Simon
Simon

Reputation: 413

Structuremap and creating objects with initial state

I have an object which needs a dependency injected into it

public class FootballLadder
{
    public FootballLadder(IMatchRepository matchRepository, int round)
    {
        // set initial state
        this.matchRepo = matchRepository;
        this.round = round;
    }

    public IEnumerable<LadderEntry> GetLadderEntries()
    {
        // calculate the ladder based on matches retrieved from the match repository
        // return the calculated ladder
    }

    private IMatchRepository matchRepo;
    private int round;
}

For arguments sake, lets assume that I can't pass the round parameter into the GetLadderEntries call itself.

Using StructureMap, how can I inject the dependency on the IMatchRepository and set the initial state? Or is this one of those cases where struggling against the framework is a sign the code should be refactored?

Upvotes: 1

Views: 780

Answers (2)

Steven
Steven

Reputation: 172646

Most DI frameworks allow you to inject primitives in constructors as Spinon showed you. When possible, I try to refactor my code in a way that I don't need complex configurations. Often this makes my application code the most understandable, with the least surprises (low number of WTFs per minute ;-)). You have to balance this out carefully, because sometimes complex configurations could make your application code simpler.

Here are some possible suggestions for refactorings:

1) Use a factory:

Using a factory is useful when clients must control the round value:

public interface IFootballLadderFactory
{
    FootballLadder CreateNew(int round);
}

This way you can inject a IFootballLadderFactory and allow clients to call:

var ladder = this.footballLadderFactory.CreateNew(3);

2) Use a property:

You can remove the round argument from the constructor and change it in to a get/set property. This is both useful when clients must be able to control the round value or when using a factory:

public class FootballLadder
{
    private IMatchRepository matchRepo;

    public FootballLadder(IMatchRepository matchRepository)
    {
    }

    public int Round { get; set; }
}

And implementation of the IFootballLadderFactory for instance could look like this:

public class CastleFootballLadderFactory : IFootballLadderFactory
{
    public IWindsorContainer Container;

    public FootballLadder CreateNew(int round)
    {
        var ladder = this.Container.Resolve<FootballLadder>();
        ladder.Round = round;
        return ladder;
    }
}

Or a client could set the Round property:

public class Client
{
    public Client(FootballLadder ladder)
    {
       ladder.Round = 3;
    }
}

Please be careful with this last example. The client should normally not have to care about the lifetime of the dependency. In this case, we're changing the state of an injected dependency. This prevents us from changing the lifetime of this dependency, because in that case the state of the ladder instance, could be changed from under the client's feet. Besides this, the FootballLadder class should throw an InvalidOperationException when the Round was never set. I think such a check is nice and clean, but does make you write a bit more code.

3) Inject a IRoundProvider into the FootballLadder constructor:

As Spinon wrote, you can implement a IRoundProvider, but instead of using it in your configuration, you can use it as constructor argument.

public class FootballLadder
{
    private IMatchRepository matchRepo;
    private int round;

    public FootballLadder(IMatchRepository matchRepository,
        IRoundProvider roundProvider)
    {
       this.round = roundProvider.GetRound();
    }        
}

4) Create a sub type specific for your DI configuration:

public class DIFootballLadder : FootballLadder
{
    private const int Round = 3;

    public DIFootballLadder(IMatchRepository matchRepository)
        : base(matchRepository, Round)
    {
    }
}

Now you can register the it as follows:

x.For<FootballLadder>().Use<DIFootballLadder>();

Downside of this is that you have this extra code with itself is plain configuration code. Besides that, when the dependencies of the FootballLadder change, you have to change the DIFootballLadder as well.

I hope this helps.

Upvotes: 1

spinon
spinon

Reputation: 10847

You can always use constructor parameters for default values. I used the following for a default instance of a sqlconnection.

this.For<SqlConnection>().Use(c => new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString));

There are other ways as well but I don't remember them off the top of my head.

EDIT: Here is another way it could be done as well. I found this one from here: http://www.theabsentmindedcoder.com/2010/05/structure-map-26-constructor-arguments.html

x.For<MatchRepository>().Use<MatchRepository>();
x.For<IFootballLadder>().Use<FootballLadder>()
    .Ctor<int>("round")
    .Is(3);

If the value of round was determined from a method you could specify it with a lambda expression to load the value like so

.Is(c => c.GetInstance<IRoundProvider>().GetRound())

Hope this makes sense. But to answer your question yes it is possible and pretty easily.

Upvotes: 2

Related Questions