55

Assembly.GetEntryAssembly()は、Webアプリケーションでは機能しません。

しかし...私は本当にそのようなものが必要です。私は、Webアプリケーションと非Webアプリケーションの両方で使用されるいくつかの深くネストされたコードを使用しています。

私の現在の解決策は、StackTraceを参照して、最初に呼び出されたアセンブリを見つけることです。

/// <summary>
/// Version of 'GetEntryAssembly' that works with web applications
/// </summary>
/// <returns>The entry assembly, or the first called assembly in a web application</returns>
public static Assembly GetEntyAssembly()
{
    // get the entry assembly
    var result = Assembly.GetEntryAssembly();

    // if none (ex: web application)
    if (result == null)
    {
        // current method
        MethodBase methodCurrent = null;
        // number of frames to skip
        int framestoSkip = 1;


        // loop until we cannot got further in the stacktrace
        do
        {
            // get the stack frame, skipping the given number of frames
            StackFrame stackFrame = new StackFrame(framestoSkip);
            // get the method
            methodCurrent = stackFrame.GetMethod();
            // if found
            if ((methodCurrent != null)
                // and if that method is not excluded from the stack trace
                && (methodCurrent.GetAttribute<ExcludeFromStackTraceAttribute>(false) == null))
            {
                // get its type
                var typeCurrent = methodCurrent.DeclaringType;
                // if valid
                if (typeCurrent != typeof (RuntimeMethodHandle))
                {
                    // get its assembly
                    var assembly = typeCurrent.Assembly;

                    // if valid
                    if (!assembly.GlobalAssemblyCache
                        && !assembly.IsDynamic
                        && (assembly.GetAttribute<System.CodeDom.Compiler.GeneratedCodeAttribute>() == null))
                    {
                        // then we found a valid assembly, get it as a candidate
                        result = assembly;
                    }
                }
            }

            // increase number of frames to skip
            framestoSkip++;
        } // while we have a working method
        while (methodCurrent != null);
    }
    return result;
}

アセンブリが必要なものであることを確認するために、次の3つの条件があります。

  • アセンブリはGACにありません
  • アセンブリは動的ではありません
  • アセンブリは生成されません(一時的なasp.netファイルを回避するため)

私が遭遇する最後の問題は、ベースページが別のアセンブリで定義されている場合です。(私はASP.Net MVCを使用していますが、ASP.Netでも同じです)。その特定のケースでは、ページを含むアセンブリではなく、その個別のアセンブリが返されます。

私が今探しているのは:

1)アセンブリの検証条件は十分ですか?(ケースを忘れたかもしれません)

2)ASP.Net一時フォルダー内の特定のコード生成アセンブリから、そのページ/ビューを含むプロジェクトに関する情報を取得する方法はありますか?(私はそうは思いませんが、誰が知っていますか...)

4

4 に答える 4

60

これは、Webアプリの「エントリ」またはメインアセンブリを取得するための信頼できる簡単な方法のようです。

コントローラーを別のプロジェクトに配置すると、ApplicationInstanceの基本クラスがビューを含むMVCプロジェクトと同じアセンブリにない場合がありますが、このセットアップは非常にまれなようです(これを試したので言及します)ある時点でセットアップし、しばらく前にいくつかのブログがこのアイデアをサポートしました)。

    static private Assembly GetWebEntryAssembly()
    {
        if (System.Web.HttpContext.Current == null ||
            System.Web.HttpContext.Current.ApplicationInstance == null) 
        {
            return null;
        }

        var type = System.Web.HttpContext.Current.ApplicationInstance.GetType();
        while (type != null && type.Namespace == "ASP") {
            type = type.BaseType;
        }

        return type == null ? null : type.Assembly;
    }
于 2011-07-19T21:11:52.133 に答える
16

私の場合、System.Web.HttpContext.Current.ApplicationInstanceを初期化する前に、Webアプリの「エントリアセンブリ」を取得する必要がありました。また、私のコードはさまざまな種類のアプリ(ウィンドウサービス、デスクトップアプリなど)で機能する必要があり、一般的なコードをWebの問題で汚染するのは好きではありません。

カスタムアセンブリレベルの属性を作成しました。これは、エントリポイントアセンブリとして指定するアセンブリのAssembyInfo.csファイルで宣言できます。次に、属性の静的GetEntryAssemblyメソッドを呼び出すだけで、エントリアセンブリを取得できます。Assembly.GetEntryAssemblyがnull以外を返す場合は、それが使用されます。それ以外の場合は、ロードされたアセンブリを検索して、カスタム属性を持つアセンブリを探します。結果はLazy<T>にキャッシュされます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace EntryAssemblyAttributeDemo
{
    /// <summary>
    /// For certain types of apps, such as web apps, <see cref="Assembly.GetEntryAssembly"/> 
    /// returns null.  With the <see cref="EntryAssemblyAttribute"/>, we can designate 
    /// an assembly as the entry assembly by creating an instance of this attribute, 
    /// typically in the AssemblyInfo.cs file.
    /// <example>
    /// [assembly: EntryAssembly]
    /// </example>
    /// </summary>
    [AttributeUsage(AttributeTargets.Assembly)]
    public sealed class EntryAssemblyAttribute : Attribute
    {
        /// <summary>
        /// Lazily find the entry assembly.
        /// </summary>
        private static readonly Lazy<Assembly> EntryAssemblyLazy = new Lazy<Assembly>(GetEntryAssemblyLazily);

        /// <summary>
        /// Gets the entry assembly.
        /// </summary>
        /// <returns>The entry assembly.</returns>
        public static Assembly GetEntryAssembly()
        {
            return EntryAssemblyLazy.Value;
        }

        /// <summary>
        /// Invoked lazily to find the entry assembly.  We want to cache this value as it may 
        /// be expensive to find.
        /// </summary>
        /// <returns>The entry assembly.</returns>
        private static Assembly GetEntryAssemblyLazily()
        {
            return Assembly.GetEntryAssembly() ?? FindEntryAssemblyInCurrentAppDomain();
        }

        /// <summary>
        /// Finds the entry assembly in the current app domain.
        /// </summary>
        /// <returns>The entry assembly.</returns>
        private static Assembly FindEntryAssemblyInCurrentAppDomain()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            var entryAssemblies = new List<Assembly>();
            foreach (var assembly in assemblies)
            {
                // Note the usage of LINQ SingleOrDefault.  The EntryAssemblyAttribute's AttrinuteUsage 
                // only allows it to occur once per assembly; declaring it more than once results in 
                // a compiler error.
                var attribute =
                    assembly.GetCustomAttributes().OfType<EntryAssemblyAttribute>().SingleOrDefault();
                if (attribute != null)
                {
                    entryAssemblies.Add(assembly);
                }
            }

            // Note that we use LINQ Single to ensure we found one and only one assembly with the 
            // EntryAssemblyAttribute.  The EntryAssemblyAttribute should only be put on one assembly 
            // per application.
            return entryAssemblies.Single();
        }
    }
}
于 2016-10-26T18:11:20.593 に答える
3

質問で提案されたアルゴリズムは確かに機能しましたが、System.Web.HttpContext.Current.ApplicationInstanceを使用するメソッドは機能しませんでした。私の問題は、解決策が必要な古いスタイルのASP.Netアプリケーションにglobal.asaxハンドラーがないことだと思います。

この短い解決策も私には有効であり、ページハンドラーがフロントエンドアセンブリで定義されているという条件で一般的に機能すると思います。

    private static Assembly GetMyEntryAssembly()
    {
      if ((System.Web.HttpContext.Current == null) || (System.Web.HttpContext.Current.Handler == null))
        return Assembly.GetEntryAssembly(); // Not a web application
      return System.Web.HttpContext.Current.Handler.GetType().BaseType.Assembly;
    }

私のアプリケーションはASP.Net4.xWebフォームアプリケーションです。このアプリケーションタイプの場合、HttpContext.Current.Handlerは、現在の要求ハンドラーのエントリポイントを含むコードモジュールです。Handler.GetType()。Assemblyは一時的なASP.Netアセンブリですが、Handler.GetType()。BaseType.Assemblyは私のアプリケーションの真の「エントリアセンブリ」です。同じことが他のさまざまなASP.Netアプリケーションタイプでも機能するかどうか知りたいです。

于 2015-11-25T13:59:49.877 に答える
0

これをWebアプリケーション(少なくとも.NET 4.5.1で)で一貫して機能させることができた唯一の方法は、Webアプリケーションプロジェクト自体でAssembly.GetExecutingAssembly()を実行することでした。

静的メソッドを使用してユーティリティプロジェクトを作成し、そこで呼び出しを実行しようとすると、代わりに、GetExecutingAssembly()とGetCallingAssembly()の両方について、そのアセンブリからアセンブリ情報が取得されます。

GetExecutingAssembly()は、Assemblyタイプのインスタンスを返す静的メソッドです。このメソッドは、Assemblyクラス自体のインスタンスには存在しません。

そこで、コンストラクターでAssemblyタイプを受け入れるクラスを作成し、Assembly.GetExecutingAssembly()からの結果を渡すこのクラスのインスタンスを作成しました。

    public class WebAssemblyInfo
    {
        Assembly assy;

        public WebAssemblyInfo(Assembly assy)
        {
            this.assy = assy;
        }

        public string Description { get { return GetWebAssemblyAttribute<AssemblyDescriptionAttribute>(a => a.Description); } }


         // I'm using someone else's idea below, but I can't remember who it was
        private string GetWebAssemblyAttribute<T>(Func<T, string> value) where T : Attribute
        {
            T attribute = null;

            attribute = (T)Attribute.GetCustomAttribute(this.assy, typeof(T));

            if (attribute != null)
                return value.Invoke(attribute);
            else
                return string.Empty;
        }
    }
}

そしてそれを使用するには

string Description = new WebAssemblyInfo(Assembly.GetExecutingAssembly()).Description;
于 2016-04-15T04:31:12.593 に答える