views:

190

answers:

1

I'm having difficulty integrating Ninject with XNA.

static class Program
{
    /**
     * The main entry point for the application.
     */
    static void Main(string[] args) 
    {
        IKernel kernel = new StandardKernel(NinjectModuleManager.GetModules());
        CachedContentLoader content = kernel.Get<CachedContentLoader>(); // stack overflow here
        MasterEngine game = kernel.Get<MasterEngine>();
        game.Run();
    }
}

    // constructor for the game
    public MasterEngine(IKernel kernel)
        : base(kernel)
    {
        this.inputReader = kernel.Get<IInputReader>();

        graphicsDeviceManager = kernel.Get<GraphicsDeviceManager>();
        Components.Add(kernel.Get<GamerServicesComponent>());

        // Tell the loader to look for all files relative to the "Content" directory.
        Assets = kernel.Get<CachedContentLoader>();

        //Sets dimensions of the game window
        graphicsDeviceManager.PreferredBackBufferWidth = 800;
        graphicsDeviceManager.PreferredBackBufferHeight = 600;
        graphicsDeviceManager.ApplyChanges();

        IsMouseVisible = false;
    }

Ninject.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Modules;
using HWAlphaRelease.Controller;
using Microsoft.Xna.Framework;
using Nuclex.DependencyInjection.Demo.Scaffolding;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace HWAlphaRelease
{
    public static class NinjectModuleManager
    {

        public static NinjectModule[] GetModules()
        {
            return new NinjectModule[1] { new GameModule() };
        }

        /// <summary>Dependency injection rules for the main game instance</summary>
        public class GameModule : NinjectModule
        {

            #region class ServiceProviderAdapter

            /// <summary>Delegates to the game's built-in service provider</summary>
            /// <remarks>
            ///   <para>
            ///     When a class' constructor requires an IServiceProvider, the dependency
            ///     injector cannot just construct a new one and wouldn't know that it has
            ///     to create an instance of the Game class (or take it from the existing
            ///     Game instance).
            ///   </para>
            ///   <para>
            ///     The solution, then, is this small adapter that takes a Game instance
            ///     and acts as if it was a freely constructable IServiceProvider implementation
            ///     while in reality, it delegates all lookups to the Game's service container.
            ///   </para>
            /// </remarks>
            private class ServiceProviderAdapter : IServiceProvider
            {

                /// <summary>Initializes a new service provider adapter for the game</summary>
                /// <param name="game">Game the service provider will be taken from</param>
                public ServiceProviderAdapter(Game game)
                {
                    this.gameServices = game.Services;
                }

                /// <summary>Retrieves a service from the game service container</summary>
                /// <param name="serviceType">Type of the service that will be retrieved</param>
                /// <returns>The service that has been requested</returns>
                public object GetService(Type serviceType)
                {
                    return this.gameServices;
                }

                /// <summary>Game services container of the Game instance</summary>
                private GameServiceContainer gameServices;

            }

            #endregion // class ServiceProviderAdapter

            #region class ContentManagerAdapter

            /// <summary>Delegates to the game's built-in ContentManager</summary>
            /// <remarks>
            ///   This provides shared access to the game's ContentManager. A dependency
            ///   injected class only needs to require the ISharedContentService in its
            ///   constructor and the dependency injector will automatically resolve it
            ///   to this adapter, which delegates to the Game's built-in content manager.
            /// </remarks>
            private class ContentManagerAdapter : ISharedContentService
            {

                /// <summary>Initializes a new shared content manager adapter</summary>
                /// <param name="game">Game the content manager will be taken from</param>
                public ContentManagerAdapter(Game game)
                {
                    this.contentManager = game.Content;
                }

                /// <summary>Loads or accesses shared game content</summary>
                /// <typeparam name="AssetType">Type of the asset to be loaded or accessed</typeparam>
                /// <param name="assetName">Path and name of the requested asset</param>
                /// <returns>The requested asset from the the shared game content store</returns>
                public AssetType Load<AssetType>(string assetName)
                {
                    return this.contentManager.Load<AssetType>(assetName);
                }

                /// <summary>The content manager this instance delegates to</summary>
                private ContentManager contentManager;

            }

            #endregion // class ContentManagerAdapter

            /// <summary>Initializes the dependency configuration</summary>
            public override void Load()
            {

                // Allows access to the game class for any components with a dependency
                // on the 'Game' or 'DependencyInjectionGame' classes.
                Bind<MasterEngine>().ToSelf().InSingletonScope();
                Bind<NinjectGame>().To<MasterEngine>().InSingletonScope();
                Bind<Game>().To<MasterEngine>().InSingletonScope();

                // Let the dependency injector construct a graphics device manager for
                // all components depending on the IGraphicsDeviceService and
                // IGraphicsDeviceManager interfaces
                Bind<GraphicsDeviceManager>().ToSelf().InSingletonScope();
                Bind<IGraphicsDeviceService>().To<GraphicsDeviceManager>().InSingletonScope();
                Bind<IGraphicsDeviceManager>().To<GraphicsDeviceManager>().InSingletonScope();

                // Some clever adapters that hand out the Game's IServiceProvider and allow
                // access to its built-in ContentManager
                Bind<IServiceProvider>().To<ServiceProviderAdapter>().InSingletonScope();
                Bind<ISharedContentService>().To<ContentManagerAdapter>().InSingletonScope();

                Bind<IInputReader>().To<UserInputReader>().InSingletonScope().WithConstructorArgument("keyMapping", Constants.DEFAULT_KEY_MAPPING);
                Bind<CachedContentLoader>().ToSelf().InSingletonScope().WithConstructorArgument("rootDir", "Content");

            }

        }

    }

}

NinjectGame.cs

  /// <summary>Base class for Games making use of Ninject</summary>
  public class NinjectGame : Game {

    /// <summary>Initializes a new Ninject game instance</summary>
    /// <param name="kernel">Kernel the game has been created by</param>
    public NinjectGame(IKernel kernel) {
      Type ownType = this.GetType();

      if(ownType != typeof(Game)) {
        kernel.Bind<NinjectGame>().To<MasterEngine>().InSingletonScope();
      }

        kernel.Bind<Game>().To<NinjectGame>().InSingletonScope();
    }

  }

} // namespace Nuclex.DependencyInjection.Demo.Scaffolding

When I try to get the CachedContentLoader, I get a stack overflow exception. I'm basing this off of this tutorial, but I really have no idea what I'm doing. Help?

A: 

You probably have a circular reference in one of your configured dependencies. So when ninject tries to instantiate the class in question it goes into an endless loop of injection.

I haven't looked through your classes to see what it might be, but I'd suggest putting a breakpoint in the constructor of each of the classes you've defined in GameModule.Load. It should become pretty obvious as soon as you can see where the nested instantiation is happening.

Joel Martinez