2

MVC 用の ninject を使用してモジュラー フレームワークを作成しました。

各モジュールは独自のルートを登録でき、独自のビューを含みます。

モジュール ディレクトリ (dll の場所):
~/Modules/<module name>/

モジュール ビューは内部にあります。
<Module dir>/Views/
これらは、通常の mvc アプリとまったく同じように配置されます。つまり、各コントローラーのフォルダーと共有フォルダーです。

レイアウトを使用してビューをレンダリングしたいのですが、レイアウトの場所をコア フレームワークで設定する必要があります (テーマを変更できるようにするため)。

ビューがlayout = _layout.cshtmlあり、アプリを実行すると次のように返されます。

The layout page "_Layout.cshtml" could not be found at the following path: "~/Modules/Module2/Views/Home/_Layout.cshtml".

呼び出されたビューはここにありました~/Modules/Module2/Views/Home/Index.cshtmlしかし、各ビューで設定せずに別の場所でレイアウトを探してほしい。とにかくコアフレームワークでそれを行うことができますか? 共有も参照するように MasterLocationFormats を設定しましたが、明らかにそうではありません (そこに _layout.cshtml を配置してテストしました)。


カスタム ビュー エンジン:

public NinjectRazorViewEngine(): base()
    {
        ViewLocationFormats = new[] {
            "~/Modules/%1/Views/{1}/{0}.cshtml",
            "~/Modules/%1/Views/{1}/{0}.vbhtml",
            "~/Modules/%1/Views/Shared/{0}.cshtml",
            "~/Modules/%1/Views/Shared/{0}.vbhtml"
        };

        MasterLocationFormats = new[] {
            "~/Modules/%1/Views/{1}/{0}.cshtml",
            "~/Modules/%1/Views/{1}/{0}.vbhtml",
            "~/Modules/%1/Views/Shared/{0}.cshtml",
            "~/Modules/%1/Views/Shared/{0}.vbhtml",
        };

        PartialViewLocationFormats = new[] {
            "~/Modules/%1/Views/{1}/{0}.cshtml",
            "~/Modules/%1/Views/{1}/{0}.vbhtml",
            "~/Modules/%1/Views/Shared/{0}.cshtml",
            "~/Modules/%1/Views/Shared/{0}.vbhtml"
        };

        PartialViewLocationFormats = ViewLocationFormats;
        AreaPartialViewLocationFormats = AreaViewLocationFormats;
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        object moduleName;
        if(controllerContext.RequestContext.RouteData.Values.TryGetValue("module",out moduleName))
            return base.CreatePartialView(controllerContext, partialPath.Replace("%1", (string)moduleName));
        return base.CreatePartialView(controllerContext, partialPath);
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        object moduleName;
        if (controllerContext.RequestContext.RouteData.Values.TryGetValue("module", out moduleName))
            return base.CreateView(controllerContext, viewPath.Replace("%1", (string)moduleName), masterPath.Replace("%1", (string)moduleName));
        return base.CreateView(controllerContext, viewPath, masterPath);
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        object moduleName;
        if (controllerContext.RequestContext.RouteData.Values.TryGetValue("module", out moduleName))
            return base.FileExists(controllerContext, virtualPath.Replace("%1", (string)moduleName));
        return base.FileExists(controllerContext, virtualPath);
    }
4

3 に答える 3

1

これには多くの作業が必要でした。

FindViewおよびFindPartialViewメソッドを適切に公開するには、ビュー エンジンに変更を加える必要がありました。質問で概説されている方法は間違っています。

これはviewEngineClassがどのように見えるかです

public NinjectRazorViewEngine(): base()
    {
        ViewLocationFormats = new[] {
            "~/Modules/{2}/Views/{1}/{0}.cshtml",
            "~/Modules/{2}/Views/{1}/{0}.vbhtml",
            "~/Modules/{2}/Views/Shared/{0}.cshtml",
            "~/Modules/{2}/Views/Shared/{0}.vbhtml",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Shared/{0}.vbhtml"
        };

        MasterLocationFormats = new[] {
            "~/Modules/{2}/Views/{1}/{0}.cshtml",
            "~/Modules/{2}/Views/{1}/{0}.vbhtml",
            "~/Modules/{2}/Views/Shared/{0}.cshtml",
            "~/Modules/{2}/Views/Shared/{0}.vbhtml",
        };

        PartialViewLocationFormats = new[] {
            "~/Modules/{2}/Views/{1}/{0}.cshtml",
            "~/Modules/{2}/Views/{1}/{0}.vbhtml",
            "~/Modules/{2}/Views/Shared/{0}.cshtml",
            "~/Modules/{2}/Views/Shared/{0}.vbhtml",
            "~/Views/{1}/{0}.cshtml",
            "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Shared/{0}.vbhtml"
        };

        PartialViewLocationFormats = ViewLocationFormats;
        AreaPartialViewLocationFormats = AreaViewLocationFormats;

        //Used to test cache
        //ViewLocationCache = new DefaultViewLocationCache();
    }
    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        return FindView(controllerContext, partialViewName, "", useCache);
    }
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        //Implement defualt exceptions
        if(controllerContext == null)
            throw new ArgumentNullException("The controllerContext parameter is null");
        if(string.IsNullOrEmpty(viewName))
            throw new ArgumentException("The viewName parameter is null or empty.");

        //Check cache if specified
        if(useCache && this.ViewLocationCache != null){
            string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, generateCacheKey(controllerContext, viewName));
            if (!string.IsNullOrEmpty(cachedLocation))
                return new ViewEngineResult(CreateView(controllerContext, cachedLocation, masterName), this);
        }

        //Create arguments for location formatting
        string trimmedViewName = string.Empty;
        if (viewName.EndsWith(".cshtml"))
            trimmedViewName = viewName.Remove(viewName.Length - 7);
        else
            trimmedViewName = viewName;
        object[] args = new object[] { trimmedViewName, controllerContext.RouteData.GetRequiredString("controller"), controllerContext.RouteData.GetRequiredString("module") };

        //Attempt to locate file
        List<string> searchedLocations = new List<string>();
        foreach(string location in ViewLocationFormats){
            string formatedLocation = string.Format(location,args);
            searchedLocations.Add(formatedLocation);
            if (FileExists(controllerContext, formatedLocation))
            {
                //File has been found. Add to cache and return view
                if(this.ViewLocationCache != null)
                    ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, generateCacheKey(controllerContext, viewName), formatedLocation);

                return new ViewEngineResult(CreateView(controllerContext, formatedLocation, masterName), this);
            }
        }

        //Couldnt find view, return searched locations
        return new ViewEngineResult(searchedLocations);
    }
    public string generateCacheKey(ControllerContext controllerContext, string viewName)
    {
        return string.Format("{0}|{1}", controllerContext.RouteData.GetRequiredString("module"), viewName);
    }

System.Web.Mvc.WebViewPage<T>次に、次のようなカスタムを実装する必要があります。

public abstract class WebViewPage<T> : System.Web.Mvc.WebViewPage<T>
{
    public override string Layout
    {
        get
        {
            return base.Layout;
        }
        set
        {
            NinjectRazorViewEngine viewEngine = new NinjectRazorViewEngine();
            System.Web.Mvc.ViewEngineResult engineResult = viewEngine.FindView(this.ViewContext.Controller.ControllerContext, value, string.Empty, true);
            System.Web.Mvc.RazorView razorView = engineResult.View as System.Web.Mvc.RazorView;
            if (razorView == null)
            {
                string searchedIn = "";
                foreach (string item in engineResult.SearchedLocations)
                {
                    searchedIn += item + "\n";
                }
                throw new HttpException(500, "Could not find views in locations:\n" + searchedIn);
            }
            base.Layout = razorView.ViewPath;
        }
    }
}

それが役立つことを願っています:)

于 2013-01-03T12:46:27.437 に答える
0

RazorGeneratorでプリコンパイルされたビューを実際に使用するようにしてください。

ビューをコンパイルすると、モジュールを単一のDLLとしてドロップできます。これは、DLLにすべてのコンテンツ(cshtmlビューなど)を含めるよりもはるかに簡単です。さらに、起動時または実行時にMEFまたはその他のReflectionメカニズムを使用してモジュールをロードできます。 、MVCアプリを本当にモジュール化して、結合を減らします。

この実装は、モジュラーWebサイトでは費用効果が高くないことに気づきました。

于 2012-12-10T11:57:20.937 に答える
0

カスタムの場所でビューを探す独自の ViewEngine を実装できます。

public class MyViewEngine : RazorViewEngine {
   public MyViewEngine() {
       this.MasterLocationFormats = new string[] {
           "PATH TO YOUR LAYOUT FILES", "ALTERNATIVE PATH"
       }
   }
}

次に、アプリケーションの起動時に (たとえば、Global.asax.cs で) カスタム エンジンを使用するようにアプリケーションをセットアップします。

ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ThemableViewEngine());
于 2012-11-29T07:13:23.167 に答える