7

NInject.Web.MvcでNInjectを使用しています。

まず、同じWebリクエスト中に、インスタンスをIPostRepositoryコントローラーとカスタムモデルバインダーの間で共有する簡単なテストプロジェクトを作成しました。私の実際のプロジェクトではIEntityChangeTracker、同じオブジェクトグラフにアクセスする2つのリポジトリが効果的に存在するという問題が発生しているため、これが必要です。したがって、テストプロジェクトを単純にするために、ダミーのリポジトリを共有しようとしています。

私が抱えている問題は、それが最初のリクエストで機能することであり、それだけです。関連するコードは以下のとおりです。

NInjectModule:

public class PostRepositoryModule : NinjectModule
{
    public override void Load()
    {
        this.Bind<IPostRepository>().To<PostRepository>().InRequestScope();
    }
}

CustomModelBinder:

public class CustomModelBinder : DefaultModelBinder
{
    [Inject]
    public IPostRepository repository { get; set; }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        repository.Add("Model binder...");

        return base.BindModel(controllerContext, bindingContext);
    }
}

public class HomeController : Controller
{
    private IPostRepository repository;

    public HomeController(IPostRepository repository)
    {
        this.repository = repository;
    }

    public ActionResult Index(string whatever)
    {
        repository.Add("Action...");

        return View(repository.GetList());
    }
}

Global.asax:

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}

この方法で行うと、実際にIPostRepositoryは、共有インスタンスではなく、の2つの別個のインスタンスが作成されます。モデルバインダーに依存関係を注入することに関して、私が見逃していることがここにあります。上記の私のコードは、 NInject.Web.Mvc wikiで説明されている最初のセットアップ方法に基づいていますが、両方を試しました。

2番目の方法を使用した場合、IPostRepository最初のWebリクエストに対してのみ共有され、その後はデフォルトでインスタンスを共有しませんでした。ただし、それが機能するようになったときDependencyResolverは、NInjectで同じことを行う方法を理解できなかったため(カーネルがNInjectMVC3クラスに隠れているため)、デフォルトを使用していました。私はそうしました:

ModelBinders.Binders.Add(typeof(string),
    DependencyResolver.Current.GetService<CustomModelBinder>());

これが初めて機能した理由は、これがNInjectを介して解決されていないためだと思います。したがって、ライフサイクルは実際にはMVCによって直接処理されています(ただし、依存関係をどのように解決しているかはわかりません)。

では、モデルバインダーを適切に登録し、NInjectに依存関係を注入させるにはどうすればよいですか?

4

3 に答える 3

8

ModelBindersは、MVCによって複数のリクエストに再利用されます。つまり、リクエストスコープよりもライフサイクルが長いため、リクエストスコープのライフサイクルが短いオブジェクトに依存することはできません。

代わりにファクトリを使用して、BindModelの実行ごとにIPostRepositoryを作成します

于 2012-02-08T02:13:56.893 に答える
5

Ninjectファクトリエクステンションを起動して実行するのは実際には本当に簡単ですが、それは既存の回答からは明らかではありませんでした。

ファクトリ拡張プラグインは前提条件であり、NUGetを介してインストールできます。

Install-Package Ninject.Extensions.Factory

必要なのは、モデルバインダーのどこかにファクトリを注入することだけです。例:

private IPostRepositoryFactory _factory;

public CustomModelBinder(IPostRepositoryFactory factory) {
    _factory = factory;
}

次に、ファクトリのインターフェイスを作成します。ファクトリの名前とメソッドの名前は実際にはまったく関係なく、戻り値のタイプだけです。(NHibernateセッションを挿入したいが、の正しい名前空間を参照することを心配する必要がないかどうかを知っておくとよいでしょう。また、コンテキストで実際に何がISessionFactory行われるかを知るのにも役立ちます):GetCurrentRepository

public interface IPostRepositoryFactory { 
    IPostRepository CreatePostRepository();
}

次に、IPostRepositoryNinjectによって既に正しく管理されていると仮定すると、拡張機能は.ToFactory()メソッドを呼び出すだけで他のすべてを実行します。

kernel.Bind<IPostRepository().To<PostRepository>();
kernel.Bind<IPostRepositoryFactory>().ToFactory();

次に、必要なコードでファクトリメソッドを呼び出すだけです。

var repo = _factory.CreatePostRepository();
repo.DoStuff();

GetXXX(更新:サービスがセッションにまだ存在しない場合、ファクトリ関数の名前付けは実際には失敗するようです。したがって、実際には、メソッドの名前付けにいくらか注意する必要があります。)

于 2012-10-08T19:46:49.827 に答える
4

私は最終的に提案されたように工場でそれを解決することができました。しかし、私はこれをどのように達成するかを理解できませんでしたNinject.Extensions.Factory。これが私が最終的に得たものです:

ファクトリインターフェイス:

public interface IPostRepositoryFactory
{
    IPostRepository CreatePostRepository();
}

工場での実装:

public class PostRepositoryFactory : IPostRepositoryFactory
{
    private readonly string key = "PostRepository";

    public IPostRepository CreatePostRepository()
    {
        IPostRepository repository;

        if (HttpContext.Current.Items[key] == null)
        {
            repository = new PostRepository();
            HttpContext.Current.Items.Add(key, repository);
        }
        else
        {
            repository = HttpContext.Current.Items[key] as PostRepository;
        }

        return repository;
    }
}

工場用のNinjectモジュール:

public class PostRepositoryFactoryModule : NinjectModule
{
    public override void Load()
    {
        this.Bind<IPostRepositoryFactory>().To<PostRepositoryFactory>();
    }
}

カスタムモデルバインダー:

public class CustomModelBinder : DefaultModelBinder
{
    private IPostRepositoryFactory factory;

    public CustomModelBinder(IPostRepositoryFactory factory)
    {
        this.factory = factory;
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        IPostRepository repository = factory.CreatePostRepository();

        repository.Add("Model binder");

        return base.BindModel(controllerContext, bindingContext);
    }
}

コントローラー:

public class HomeController : Controller
{
    private IPostRepository repository;

    public HomeController(IPostRepositoryFactory factory)
    {
        this.repository = factory.CreatePostRepository();
    }

    public ActionResult Index(string whatever)
    {
        repository.Add("Action method");

        return View(repository.GetList());
    }
}

Global.asaxを使用して、カスタムモデルバインダーを接続します。

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}

私の見解では、これは私に次の望ましい出力を与えました:

Model binder
Action method

于 2012-02-10T17:03:35.920 に答える