53

個別のアセンブリをプラグインできるようにする Web アプリケーションを作成しようとしています。私は MVC プレビュー 4 を Unity と組み合わせて依存性注入に使用しています。これを使用して、プラグイン アセンブリからコントローラーを作成します。ビュー エンジンとして WebForms (デフォルト aspx) を使用しています。

ビューを使用したい場合、ASPX 部分の動的コンパイルが原因で、コア プロジェクトで定義されているビューに固執します。展開手順全体を実行する必要なく、ASPX ファイルを別のアセンブリに含める適切な方法を探しています。明らかな何かが欠けていますか?または、プログラムでビューを作成することに頼るべきですか?


更新:受け入れられた回答を変更しました。Dale の回答は非常に徹底していますが、別の仮想パス プロバイダーを使用したソリューションを選択しました。それは魔法のように機能し、コード全体で約 20 行しかかからないと思います。

4

4 に答える 4

29

さまざまな部分サンプルからこれを適切に機能させるのに時間がかかりすぎたので、通常の Views フォルダーと同じ構造であるが、すべてが埋め込みとしてビルドするように設定された共有ライブラリ内の Views フォルダーからビューを取得するために必要な完全なコードを次に示します。資力。通常のファイルが存在しない場合にのみ、埋め込みファイルを使用します。

Application_Start の最初の行:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider());

VirtualPathProvider

   public class EmbeddedVirtualFile : VirtualFile
{
    public EmbeddedVirtualFile(string virtualPath)
        : base(virtualPath)
    {
    }

    internal static string GetResourceName(string virtualPath)
    {
        if (!virtualPath.Contains("/Views/"))
        {
            return null;
        }



        var resourcename = virtualPath
            .Substring(virtualPath.IndexOf("Views/"))
            .Replace("Views/", "OrangeGuava.Common.Views.")
            .Replace("/", ".");

        return resourcename;

    }


    public override Stream Open()
    {
        Assembly assembly = Assembly.GetExecutingAssembly();


        var resourcename = GetResourceName(this.VirtualPath);
        return assembly.GetManifestResourceStream(resourcename);
    }




}

public class EmbeddedViewPathProvider : VirtualPathProvider
{


    private bool ResourceFileExists(string virtualPath)
    {

        Assembly assembly = Assembly.GetExecutingAssembly();


        var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath);
        var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename);
        return result;
    }

    public override bool FileExists(string virtualPath)
    {
        return base.FileExists(virtualPath) || ResourceFileExists(virtualPath);
    }


    public override VirtualFile GetFile(string virtualPath)
    {

        if (!base.FileExists(virtualPath))
        {
            return new EmbeddedVirtualFile(virtualPath);
        }
        else
        {
            return base.GetFile(virtualPath);
        }

    }

}

動作させるための最後の手順は、ビュー フォルダー内のビューは使用されないため、ルート Web.Config に厳密に型指定された MVC ビューを解析するための適切な設定が含まれている必要があることです。

<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <controls>
    <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

Mono で動作させるには、いくつかの追加手順が必要です。ビュー フォルダー内のすべてのファイルは、必要に応じてではなくアプリの起動時に読み込まれるため、最初に GetDirectory を実装する必要があります。

public override VirtualDirectory GetDirectory(string virtualDir)
    {
        Log.LogInfo("GetDirectory - " + virtualDir);
        var b = base.GetDirectory(virtualDir);
        return new EmbeddedVirtualDirectory(virtualDir, b);
    }

public class EmbeddedVirtualDirectory : VirtualDirectory
{
    private VirtualDirectory FileDir { get; set; } 

    public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir)
        : base(virtualPath)
    {
        FileDir = filedir;
    }

    public override System.Collections.IEnumerable Children
    {
        get { return FileDir.Children; }
    }

    public override System.Collections.IEnumerable Directories
    {
        get { return FileDir.Directories; }
    }

    public override System.Collections.IEnumerable Files
    {
        get {

            if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/"))
            {
                return FileDir.Files;
            }

            var fl = new List<VirtualFile>();

            foreach (VirtualFile f in FileDir.Files)
            {
                fl.Add(f);
            }


            var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/"))
.Replace("Views/", "OrangeGuava.Common.Views.")
.Replace("/", ".");

            Assembly assembly = Assembly.GetExecutingAssembly();

            var rfl = assembly.GetManifestResourceNames()
                .Where(s => s.StartsWith(resourcename))
                .Select(s => VirtualPath + s.Replace(resourcename, ""))
                .Select(s => new EmbeddedVirtualFile(s));
            fl.AddRange(rfl);

            return fl;
        }
    }
}

最後に、強く型付けされたビューはほとんど完全に機能するわけではありません。モデルは型指定されていないオブジェクトとして扱われるため、強い型付けを元に戻すには、次のようなもので共有ビューを開始する必要があります

<% var Model2 = Model as IEnumerable<AppModel>;  %>
于 2011-01-01T10:59:13.433 に答える
15

基本的に、これは WebForms でユーザー コントロールの ASCX ファイルを DLL にコンパイルしようとした場合と同じ問題です。私はこれを見つけましたhttp://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspxあなたにも役立つかもしれません。

于 2008-08-29T20:34:48.740 に答える
13
protected void Application_Start()
{
    WebFormViewEngine engine = new WebFormViewEngine();

    engine.ViewLocationFormats = new[] { "~/bin/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
    engine.PartialViewLocationFormats = engine.ViewLocationFormats;

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(engine);

    RegisterRoutes(RouteTable.Routes);
}

ビューの「出力にコピー」プロパティを「常にコピー」に設定します

于 2009-10-03T20:29:31.743 に答える
2

まだ聖杯を探しているすべての人への追加: Web フォームのビューエンジンにあまり執着していなければ、私はそれを見つけることに少し近づいています。

最近、Spark ビューエンジンを試しました。完全に素晴らしく、たとえ脅されても Web フォームには戻らないというだけでなく、アプリケーションのモジュール性のためのいくつかの非常に優れたフックも提供します。彼らのドキュメントの例では、Windsor を IoC コンテナーとして使用していますが、別のアプローチを取りたい場合、それがはるかに難しくなるとは想像できません。

于 2010-03-21T22:40:28.263 に答える