web-dev-qa-db-fra.com

Gettryassembly pour les applications Web

Assembly.getentryAssembly () Ne fonctionne pas pour les applications Web.

Mais ... j'ai vraiment besoin de quelque chose comme ça. Je travaille avec un code profondément imbriqué utilisé dans des applications Web et non Web.

Ma solution actuelle consiste à parcourir la standingTrace pour trouver le premier assemblage appelé.

/// <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;
}

Pour s'assurer que l'Assemblée est ce que nous voulons, nous avons 3 conditions:

  • l'assemblée n'est pas dans le GAC
  • l'assemblée n'est pas une dynamique
  • l'assemblage n'est pas généré (pour éviter les fichiers ASP.NET temporaires

Le dernier problème que je rencontre est lorsque la page de base est définie dans un assemblage séparé. (J'utilise ASP.NET MVC, mais ce sera le même avec ASP.NET). Dans ce cas particulier, c'est que l'assemblage séparé qui est renvoyé, pas celui qui contient la page.

Ce que je cherche maintenant, c'est:

1) Les conditions de validation de mes assemblées sont-elles suffisamment? (J'ai peut-être des cas oubliés)

2) Existe-t-il un moyen, d'un assemblage généré par code donné dans le dossier temporaire ASP.NET, pour obtenir des informations sur le projet qui contient cette page/la vue? (Je pense pas, mais qui sait ...)

46
Mose

Cela semble être un moyen fiable et simple d'obtenir "l'entrée" ou l'assemblage principal d'une application Web.

Si vous mettez des contrôleurs dans un projet séparé, vous constaterez peut-être que la classe de base de l'applicationNstance n'est pas dans la même assemblée que votre projet MVC contenant les points de vue - mais cette configuration semble assez rare (je le mentionne parce que j'ai essayé cela Configuration à un moment donné, et pendant un moment, quelques blogs ont soutenu l'idée).

    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;
    }
50
quentin-starin

Dans mon cas, je devais obtenir le "Assemblée d'entrée" pour une application Web avant System.Web.httpcontext.Current.ApplicationInstance est initialisée. En outre, mon code nécessaire pour travailler pour divers types d'applications (services de fenêtre, applications de bureau, etc.), et je n'aime pas polluer mon code commun avec des préoccupations Web.

J'ai créé un attribut de niveau de montage personnalisé, qui peut être déclaré dans le fichier assemblyinfo.cs d'un assemblage que vous souhaitez désigner comme l'assemblage du point d'entrée. Ensuite, vous appelez simplement la méthode Static GetRentrySembly de l'attribut pour obtenir l'assemblage de saisie. Si Assembly.BettentryBly retourne non-NULL, elle est utilisée, sinon elle effectue une recherche à travers des assemblages chargés pour celui avec l'attribut personnalisé. Le résultat est mis en cache dans un paresseux <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();
        }
    }
}
10
robertburke

En réponse à ma propre question (certaines personnes ici sont vraiment résolues sur le taux accepté) => Je n'ai pas trouvé de meilleure façon que le code donné dans la question.

Cela signifie que la solution n'est pas parfaite, mais cela fonctionne bien car votre page de base est définie dans l'ensemble front-end.

4
Mose

L'algorithme proposé dans la question a effectivement fonctionné pour moi, alors que la méthode utilisant System.Web.httpcontext.Current.ApplicationInstance n'a pas fait. Je pense que mon problème est que la demande ASP.NET ancienne pour laquelle j'ai besoin d'une solution manque de gestionnaire global.Asax.

Cette solution plus courte a également fonctionné pour moi et je pense que vous travaillerez généralement sur la condition que le gestionnaire de page soit défini dans l'assemblage frontal:

    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;
    }

Mon application est une application Web ASP.NET 4.x Formulaires Web. Pour ce type d'application, httpcontext.Current.Handler est le module de code contenant le point d'entrée du gestionnaire de demande actuel. Handler.getType (). L'assemblage est un assemblage temporaire ASP.NET, mais gestionnaire.gettype (). BaseType.assembly est le véritable "assemblage d'entrée" de mon application. Je suis curieux si les mêmes fonctionnent pour divers autres types d'applications ASP.NET.

3
Berend Engelbrecht

La seule façon dont j'ai pu obtenir cela pour travailler de manière cohérente pour les applications Web (au moins dans .NET 4.5.1) était de faire l'assemblage.getexecutingAssemblage () dans le projet d'application Web lui-même.

Si vous essayez de créer un projet d'utilitaires avec des méthodes statiques et effectuez l'appel, vous obtiendrez à la place des informations de montage de cette assemblée - pour obtenir à la fois get contre-currence () et getcallingAlement ().

GetExecutingAssemblage () est une méthode statique qui renvoie une instance du type d'assemblage. La méthode n'existe pas sur une instance de la classe d'assemblage elle-même.

Ainsi, ce que j'ai fait a été créé une classe qui accepte le type d'assemblage dans le constructeur et crée une instance de cette classe qui passait les résultats de l'assemblage.getexecutingAssemblage ().

    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;
        }
    }
}

Et pour l'utiliser

string Description = new WebAssemblyInfo(Assembly.GetExecutingAssembly()).Description;
0
Paul Berglund