Object Lifetime per scope defined by the lifetime of another object

Dec 14, 2013 at 3:12 AM
Edited Dec 14, 2013 at 3:17 AM
Hello people,

How can SimpleInjector helps me to implement this type of lifetime:
Let's have an example. First we have the class in which instances of that class defines the scope:
    public class Game : IGame
    {
        public Game(int numberOfPlayers)
        {
               ...
        }
    }
This means that other interfaces that are in this scope needs to be associated with a created game when trying to resolve them. IGame is implicit in this scope. So when I create a game and I resolve IGame in his scope then it will return the game.

We can now have another class that depends on IGame:
 public class RequestMaker : IRequestMaker
    {
        public RequestMaker(IGame game, ICommandBus commandBus)
        {

        }
    }
Container.ResolveInstace<IRequestMaker>() will return a requestMaker with the associated game.

To implement this I need to define the context(scope):
    public interface IContext
    {
        IDisposable Begin();
    }

   public interface IContextFactory<in T>
    {
        IContext CreateContextFor(T entity);
    }
So when I have a game created and I need the IRequestMaker I call the RequestMakerFactory:

public class GameFactory
    {
        private readonly IRequestMakerFactory _requestMakerFactory;

        public GameFactory(IRequestMakerFactory requestMakerFactory)
        {
            _requestMakerFactory = requestMakerFactory;
        }

        public Game MakeGame(int numberOfPlayers)
        {
            Game game = new Game(numberOfPlayers);
            var requestMaker = _requestMakerFactory.CreateRequestMaker(game);
            DoSomethingWith(requestMaker);
            return game;
        }
    }

  public class RequestMakerFactory : IRequestMakerFactory
    {
        private readonly Container _container;
        private readonly IContextFactory<Game> _contextFactory;

        public RequestMakerFactory(Container container, IContextFactory<Game> contextFactory)
        {
            _container = container;
            _contextFactory = contextFactory;
        }

        public IRequestMaker CreateRequestMaker(Game game)
        {
            using (_contextFactory.CreateContextFor(game).Begin())
            {
                return _container.GetInstance<IRequestMaker>();
            }
        }
    }
To implement CommandBus I want the IContext to be also injected.
    public class CommandBus : ICommandBus
    {
        private readonly Container _container;
        private readonly IContext _context;

        public CommandBus(Container container, IContext context)
        {
            _container = container;
            _context = context;
        }

        private ICommandHandler<T> ResolveCommandHandler<T>()
        {
            using (_context.Begin())
            {
                return _container.GetInstance<ICommandHandler<T>>();
            }
        }

        public void SubmitCommand<T>(T command)
        {
            ResolveCommandHandler<T>().Handle(command);
        }
    }
Now a handler can depend on the IGame and on IRequstMaker defined in the scope of the created game:
  public class PlayCardHandler : ICommandHandler<PlayCardCommand>
    {
        private readonly IRequestMaker _requestMaker;
        private readonly IGame _game;

        public PlayCardHandler(IRequestMaker requestMaker, IGame game)
        {
            _requestMaker = requestMaker;
            _game = game;
        }

        public void Handle(PlayCardCommand command)
        {
            throw new NotImplementedException();
        }
    }
You see that all interfaces except IRequestMakerFactory are in the scope of the created game. What do you think?
Coordinator
Dec 14, 2013 at 8:01 AM
What is the lifetime of a game? Do you have multiple games active at the same time? Is a game bound to a thread? A session? What kind of application do you run? Windows Phone or do you run a game server on WCF o Web API? Do you get the proper game based on an incoming request or does a game just live a single request? Where do you store the game data between requests?
Dec 14, 2013 at 3:04 PM
I am creating a library for a board game. It contains only the gameplay. The client of the library can be WPF, WCF, etc and can create many games and its their responsibility to store the game in their session. Game is the API of the library. The client will do something like this:
var game = _gameFactory.CreateNewGame(numberOfPlayers: 5);
game.CurrentPlayer.PlayCard(newCard());
PlayCard will call internaly some interfaces that are in the scope of the created game. For example if it will call ICommandBus.SubmitCommand(new PlayCardCommand()). The PlayCardHandler will be resolved with IGame for the game.


Thanks for your response.

I will copy a test to give you an ideea of the API:
        [Test]
        public void PlayBang()
        {
            var aBangCard = ABangCard();

            var currentPlayer = APlayer()
                .On(Seat.Zero)
                .WithHand(aBangCard)
                .Build();

            var targetPlayer = APlayer()
                .On(Seat.One)
                .With(Bullets.Two)
                .Build();

            var game = AGame()
                .WithPlayers(currentPlayer, targetPlayer)
                .WithCurrentPlayer(currentPlayer)
                .Build();

            {
                currentPlayer.RequestType.Should().Be(typeof(Nothing));
                targetPlayer.RequestType.Should().Be(typeof(Nothing));
            }

            StartWithRequest<PlayAnyCards>(game, currentPlayer);
            {
                currentPlayer.RequestType.Should().Be(typeof(PlayAnyCards));
                targetPlayer.RequestType.Should().Be(typeof(Nothing));
            }

            currentPlayer.Request<PlayAnyCards>().PlayCard(aBangCard);
            {
                currentPlayer.RequestType.Should().Be(typeof(SelectBangTarget));
                targetPlayer.RequestType.Should().Be(typeof(Nothing));
            }

            currentPlayer.Request<SelectBangTarget>().SelectPlayer(targetPlayer);
            {
                currentPlayer.HandCards.Should().NotContain(aBangCard);
                currentPlayer.RequestType.Should().Be(typeof(Nothing));
                targetPlayer.RequestType.Should().Be(typeof(HitByABang));
            }

            targetPlayer.Request<HitByABang>().LoseOneBullet();
            {
                targetPlayer.Bullets.Should().Be(Bullets.One);
                currentPlayer.RequestType.Should().Be(typeof(PlayAnyCards));
                targetPlayer.RequestType.Should().Be(typeof(Nothing));
            }
        }

        private void StartWithRequest<TRequest>(Game game, Player requestedPlayer)
         where TRequest : class, IRequest
        {
            var requestTree = _container.GetInstance<IRequestTreeFactory>()
                                        .CreateRequestTree(game);

            requestTree.StartWith<TRequest>(requestedPlayer);
        }
Coordinator
Dec 18, 2013 at 9:23 AM
Usually, it is the end application that defines the Composition Root where you wire up all components with their lifestyle in your DI container. You are probably trying to hide the DI container and its configuration inside your library and prevent consumers of your library from having to wire everything up, am I right?

If that's the case, I wonder if you should even use a DI container at all, since your library will drag extra dependencies with it that could make it harder for your consumers to use your library. Think for instance about the versioning problems this could cause if your consumers try to use the same DI container, but with a different version.

But let's assume for a moment that you still want to use a container :-). I'm amagining that users interact with the GameFactory, call it to create a game (that you construct the complete graph for at that point) and from that moment on the only interaction the user has for that game is by calling the Game or its properties/methods.

In that case you are building up the complete object graph at that point and the Game instance will be reused in the graph that is created. There are several ways to do this, but probably the easiest is by registering a delegate that return the correct instance:
internal static Bootstrapper {
    [ThreadStatic]
    internal static Game gameInstance;

    private Container Configure() {
        var container = new Container();

        container.Register<IGame>(() => gameInstance);
        // more registrations
        return container;
    }
}

// In the game factory
public Game MakeGame(int numberOfPlayers)
{
    Game game = new Game(numberOfPlayers);
    
    Bootstrapper.gameInstance = game;
    
    game.Something = Bootstrapper.Container.GetInstance<SomeThingToResolve>();

    Bootstrapper.gameInstance = null;

    return game;
}
Does this help?
Marked as answer by dot_NET_Junkie on 2/26/2014 at 1:15 PM
Dec 18, 2013 at 1:46 PM
Yes. Thanks a lot!!! I never thought about the problems that arise if I am trying to hide the DI container for the library.