4

大規模なソリューションの一部として MVC4 Web アプリ プロジェクトがあります。テストプロジェクトもあります。やり直しの予定のない大量のコードを扱っているため、常に変更を加えることができるとは限りません。

MVC4 Web アプリには、「通常の」コントローラーと Web API コントローラーがあります。RC ではなく、RTM バージョンの Web API を使用しています。

プロジェクトに IoC を導入しようとしました。(DLL をダウンロードして直接参照するのではなく) NuGet インストール手法を使用して、以下をインストールしました。

Ninject v3.0.1.10, 
Ninject.MVC3 v3.0.0.6
Ninject.Extensions.Factory v3.0.1.0
Ninject.Web.Common v 3.0.0.7

私のソリューションには、Ninject を利用する他の参照コンポーネントはありません。

次に、Brad Wilson のアドバイスと彼の Github Gist https://gist.github.com/2417226、および Filip W の同様のアドバイス ( http://www.strathweb.com/2012/05/using- ) に従います。 ninject-with-the-latest-asp-net-web-api-source/、NinjectResolver を実装し、グローバル構成に「登録」しました。

Web アプリを起動すると、既定のページが のIndexアクションにマップされますProjectController。これはビューをレンダリングし、Knockout を使用して、ApiControllerと呼ばれるアクションへの呼び出しを介して ViewModel にデータを取り込みApiProjectController.Get()ます。

私のNinjectWebCommon.csコードは次のようになります。

using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Web.Http;
using System.Web.Http.Dependencies;
using Ninject.Extensions.Factory;
using Ninject.Syntax;
using OfficeWebApp.Utilities;

[assembly: WebActivator.PreApplicationStartMethod(typeof(OfficeWebApp.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(OfficeWebApp.App_Start.NinjectWebCommon), "Stop")]

namespace OfficeWebApp.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;

    public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper Bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            Bootstrapper.Initialize(CreateKernel);
        }

        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            Bootstrapper.ShutDown();
        }

        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
            kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

            RegisterServices(kernel);

            GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

            return kernel;
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<IDataManagerConnection>().To<DataManagerConnection>().WithConstructorArgument("overriddenConnectionString", string.Empty);
            kernel.Bind<IDataManagerConnectionFactory>().ToFactory();
        }        
    }

    public class NinjectDependencyScope : IDependencyScope
    {
        private IResolutionRoot resolver;

        internal NinjectDependencyScope(IResolutionRoot resolver)
        {
            Contract.Assert(resolver != null);

            this.resolver = resolver;
        }

        public void Dispose()
        {
            IDisposable disposable = resolver as IDisposable;
            if (disposable != null)
                disposable.Dispose();

            resolver = null;
        }

        public object GetService(Type serviceType)
        {
            if (resolver == null)
                throw new ObjectDisposedException("this", "This scope has already been disposed");

            return resolver.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (resolver == null)
                throw new ObjectDisposedException("this", "This scope has already been disposed");

            return resolver.GetAll(serviceType);
        }
    }

    public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver(IKernel kernel)
            : base(kernel)
        {
            this.kernel = kernel;
        }

        public IDependencyScope BeginScope()
        {
            return new NinjectDependencyScope(kernel.BeginBlock());
        }
    }
}

ProjectControllerコードは次のとおりです。

public class ProjectController : Controller
{
    private readonly IDataManagerConnectionFactory _dataManagerConnectionFactory;

    public ProjectController(IDataManagerConnectionFactory dataManagerConnectionFactory)
    {
        _dataManagerConnectionFactory = dataManagerConnectionFactory;
    }

    [HttpGet]
    public ActionResult Index()
    {
        //TODO:             
        ViewBag.Organisation = "Preview";

        return View();
    }
}

...そしてApiProjectController

public class ApiProjectController : ApiController
{
    private readonly IDataManagerConnectionFactory _dataManagerConnectionFactory;

    public ProjectsController(IDataManagerConnectionFactory dataManagerConnectionFactory)
    {
        _dataManagerConnectionFactory = dataManagerConnectionFactory;
    }

    [HttpGet]
    public IEnumerable<ProjectTileModel> Get()
    {
        using (IDataManagerConnection connection = _dataManagerConnectionFactory.Create())
        {
            List<ProjectTileModel> projectViewModels = connection.DataManager.GetProjectInfos()
                                                                             .ToList();
            return projectViewModels;
        }
    }
}

アクション メソッドが完了するApiProjectController.Get()と、Ninject は次の例外をスローします。

Error loading Ninject component ICache
No such component has been registered in the kernel's component container.

Suggestions:
  1) If you have created a custom subclass for KernelBase, ensure that you have properly
     implemented the AddComponents() method.
  2) Ensure that you have not removed the component from the container via a call to RemoveAll().
  3) Ensure you have not accidentally created more than one kernel.

コール スタックは次のようになります。

Ninject.dll!Ninject.Components.ComponentContainer.Get(System.Type component) Line 160   C#
Ninject.dll!Ninject.Components.ComponentContainer.Get<Ninject.Activation.Caching.ICache>() Line 116 + 0x46 bytes    C#
Ninject.Web.Common.dll!Ninject.Web.Common.OnePerRequestHttpModule.DeactivateInstancesForCurrentHttpRequest.AnonymousMethod__1(Ninject.IKernel kernel) Line 74 + 0x27 bytes  C#
Ninject.dll!Ninject.GlobalKernelRegistration.MapKernels(System.Action<Ninject.IKernel> action) Line 75 + 0xe bytes  C#
Ninject.Web.Common.dll!Ninject.Web.Common.OnePerRequestHttpModule.DeactivateInstancesForCurrentHttpRequest() Line 76    C#
Ninject.Web.Common.dll!Ninject.Web.Common.OnePerRequestHttpModule.Init.AnonymousMethod__0(object o, System.EventArgs e) Line 56 + 0x9 bytes C#

ComponentContainer.csこの例外は、ファイル内の次の Ninject コードでスローされています。

        Type implementation = _mappings[component].FirstOrDefault(); // <-- see note below...

        if (implementation == null)
            throw new InvalidOperationException(ExceptionFormatter.NoSuchComponentRegistered(component)); // <-- exception thrown here

注: 上記の行で、_mappingsコレクションには 1 つの項目が含まれています。キーTypeは探している ( ICache) と一致しますが、Valuesメンバー ( List<Type>) は空です (0 カウント)

を使用しないOnePerRequestHttpModuleでください。.ToFactory()バインディングで使用しているため、何かおかしなことが起こっていますか? OnePerRequestHttpModuleが呼び出されている理由はよくわかりませんDeactivateInstancesForCurrentHttpRequest()が、Ninject は内部キャッシュを取得しようとしているようです (たぶん??)

私が間違っているのは何ですか?

4

1 に答える 1

0

私は本当にこれの底にたどり着きませんでした。それが Ninject のバグなのか、それとも単純に使い方が間違っていたのかはわかりません。ただし、IoC コンテナーを AutoFAC に切り替えることで問題を回避しました。

于 2012-12-19T22:50:32.823 に答える