using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace ZeroLevel.Services.Reflection { /// /// For certain types of apps, such as web apps, /// returns null. With the , we can designate /// an assembly as the entry assembly by creating an instance of this attribute, /// typically in the AssemblyInfo.cs file. /// /// [assembly: EntryAssembly] /// /// [AttributeUsage(AttributeTargets.Assembly)] public sealed class EntryAssemblyAttribute : Attribute { /// /// Lazily find the entry assembly. /// private static readonly Lazy EntryAssemblyLazy = new Lazy(GetEntryAssemblyLazily); /// /// Gets the entry assembly. /// /// The entry assembly. public static Assembly GetEntryAssembly() { return EntryAssemblyLazy.Value; } /// /// Invoked lazily to find the entry assembly. We want to cache this value as it may /// be expensive to find. /// /// The entry assembly. private static Assembly GetEntryAssemblyLazily() { return Assembly.GetEntryAssembly() ?? FindEntryAssemblyInCurrentAppDomain(); } /// /// Finds the entry assembly in the current app domain. /// /// The entry assembly. private static Assembly FindEntryAssemblyInCurrentAppDomain() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var entryAssemblies = new List(); 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().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.SingleOrDefault(); } } }