web-dev-qa-db-ja.com

コントローラークラスが別のアセンブリにあるときに、コントローラーをASP.NET MVCに登録するにはどうすればよいですか?

私の目標は、asp.net mvcのコントローラーレジスターを変更して、コントローラーとビューを別の(子)アセンブリに作成し、ビューファイルとDLLをホストMVCアプリケーションにコピーするだけで、新しいコントローラーが効果的にプラグインされるようにすることです」をホストアプリに送信します。

明らかに、ある種のIoCパターンが必要になりますが、困っています。

私の考えは、system.web.mvcを参照する子アセンブリを作成し、Controllerから継承したコントローラークラスの構築を開始することでした。

別のアセンブリ:

_using System.Web;
using System.Web.Mvc;

namespace ChildApp
{
    public class ChildController : Controller
    {
        ActionResult Index()
        {
            return View();
        }
    }
}
_

いいねダンディだしかし、その後、実行時に新しい子コントローラーをロードするようにホストアプリケーションのコントローラーレジストリを変更することを検討したところ、混乱しました。おそらく、C#をより深く理解する必要があるためです。

とにかく、CustomControllerFactoryクラスを作成する必要があると思いました。そこで、GetControllerInstance()メソッドをオーバーライドするクラスを書き始めました。私がタイプしているとき、知性がこれをポップアップしました:

ホストMVCアプリケーション:

_public class CustomControllerFactory : DefaultControllerFactory 
{
    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        return base.GetControllerInstance(requestContext, controllerType);
    }
}
_

さて、この時点で私は途方に暮れています。私はそれが何をしているのか分かりません。もともとは、次のようなService Locatorのような「Controller Loader」クラスを作成していました。

ホストMVCアプリケーション:

_public class ControllerLoader
{
    public static IList<IController> Load(string folder)
    {
        IList<IController> controllers = new List<IController>();

        // Get files in folder
        string[] files = Directory.GetFiles(folder, "*.plug.dll");
        foreach(string file in files)
        {
            Assembly assembly = Assembly.LoadFile(file);
            var types = Assembly.GetExportedTypes();
            foreach (Type type in types)
            {
                if (type.GetInterfaces().Contains(typeof(IController)))
                {
                    object instance = Activator.CreateInstance(type);
                    controllers.Add(instance as IController);
                }
            }
        }
        return controllers;
    }
}
_

そして、コントローラーのリストを使用して、コントローラーファクトリーにコントローラーを登録することを計画していました。しかし...どうやって?私はそれを理解することの端にいるような気がします。私はそれがすべてこの質問に帰着していると思います:どうすればreturn base.GetControllerInstance(requestContext, controllerType);にフックできますか?または、まったく別のアプローチを使用する必要がありますか?

27
quakkels

「ルート」ASP.NET MVCプロジェクトから他のアセンブリを参照します。コントローラが別の名前空間にある場合は、global.asaxのルートを次のように変更する必要があります。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        namespaces: new[] { typeof(HomeController).Namespace }
    );

}

namespacesメソッドへの追加のMapRoute引数に注意してください。

26
Mark Seemann

他のプロジェクトはエリアのように設定する必要があり、希望する結果が得られます。エリア登録クラスは、エリアプロジェクトをミックスに取り入れます。次に、実行中のアプリにdllをドロップするだけで、アプリ全体をビルドして再デプロイすることなく実行されます。

これを行う最も簡単な方法は、ソリューションに新しいmvcプロジェクトを追加し、Visual Studioに/ areas/mynewprog /内に新しいプロジェクトを作成させることです。不要なファイルをすべて削除し、エリア登録クラスを追加してそれを接続します。

プロジェクトをビルドし、dllを取得して、実行中のWebサイトのbinディレクトリにドロップします。

次に、ビューやものを処理し、それらをdllにコンパイルするか、サーバーのareas/mynewproj /フォルダーにコピーします。

3
JBeckton

プラグインプロジェクトの構造を示すように、別のモジュールを作成します。

Plugin Project structure

一部のファイルがプロジェクトから除外されていることに注意してください。

MessagingController.csファイルは次のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MessagingModule.Controllers
{
    public class MessagingController : Controller
    {
        //
        // GET: /Messaging/

        public ActionResult Index()
        {
          var appName = Session["appName"]; // Get some value from another module
            return Content("Yep Messaging module @"+ appName);
        }

    }
}

MessagingAreaRegistrationファイルは次のとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace MessagingModule
{
  public class MessagingAreaRegistration : AreaRegistration
  {
    public override string AreaName
    {
      get
      {
        return "Messaging";
      }
    }

    public override void RegisterArea( AreaRegistrationContext context )
    {
      context.MapRoute(
          "Messaging_default",
          "Messaging/{controller}/{action}/{id}",
          new { action = "Index", id = UrlParameter.Optional }
        //new[] { "MessagingModule.Controllers" }
      );
    }

  }
}

Global.asax.csファイルの関連部分は次のとおりです。

[外部プラグイン(コントローラー、APIコントローラーライブラリ)を現在のアプリドメインに読み込みます。この後、asp.net mvcが残りの作業を行います(エリア登録とコントローラーの検出] [3]

   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Web;
   using System.Web.Http;
   using System.Web.Mvc;
   using System.Web.Optimization;
   using System.Web.Routing;
   using System.Reflection;
   using System.Web.Configuration;
   using System.Text.RegularExpressions;

   namespace mvcapp
   {
     // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
     // visit http://go.Microsoft.com/?LinkId=9394801

     public class MvcApplication : System.Web.HttpApplication
     {
       protected void Application_Start()
       {
         var domain = AppDomain.CurrentDomain;

         domain.Load("MessagingModule, Version=1.0.0.0, Culture=neutral,   PublicKeyToken=null");
1
RorvikSam

コントローラでの私の経験は、これがうまくいくということです。アセンブリをbinディレクトリにコピーします(またはアセンブリへの参照を追加すると、そこにコピーされます)。

私はビューで試したことはありません

0
pm100