Skip to content

Instantly share code, notes, and snippets.

@grumpydev
Created March 12, 2011 18:43
Show Gist options
  • Save grumpydev/867459 to your computer and use it in GitHub Desktop.
Save grumpydev/867459 to your computer and use it in GitHub Desktop.
Better type scanner for nancy that caches and also loads Nancy dlls into the app domain on startup (to work around the view engines not being detected in mspec tests)
namespace AppDomainTypeScanning
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
/// <summary>
/// Scans the app domain for assemblies and types
/// </summary>
public static class AppDomainAssemblyTypeScanner
{
/// <summary>
/// App domain type cache
/// </summary>
private static IEnumerable<Type> types;
/// <summary>
/// App domain assemblies cache
/// </summary>
private static IEnumerable<Assembly> assemblies;
/// <summary>
/// Initializes static members of the <see cref="AppDomainAssemblyTypeScanner"/> class.
/// </summary>
static AppDomainAssemblyTypeScanner()
{
LoadNancyAssemblies();
}
/// <summary>
/// Gets app domain types.
/// </summary>
public static IEnumerable<Type> Types
{
get
{
return types;
}
}
/// <summary>
/// Gets app domain types.
/// </summary>
public static IEnumerable<Assembly> Assemblies
{
get
{
return assemblies;
}
}
/// <summary>
/// Load assemblies from a the app domain base directory matching a given wildcard.
/// Assemblies will only be loaded if they aren't already in the appdomain.
/// </summary>
/// <param name="wildcardFilename">Wildcard to match the assemblies to load</param>
public static void LoadAssemblies(string wildcardFilename)
{
LoadAssemblies(AppDomain.CurrentDomain.BaseDirectory, wildcardFilename);
}
/// <summary>
/// Load assemblies from a given directory matching a given wildcard.
/// Assemblies will only be loaded if they aren't already in the appdomain.
/// </summary>
/// <param name="containingDirectory">Directory containing the assemblies</param>
/// <param name="wildcardFilename">Wildcard to match the assemblies to load</param>
public static void LoadAssemblies(string containingDirectory, string wildcardFilename)
{
UpdateAssemblies();
var existingAssemblyPaths = assemblies.Select(a => a.Location).ToArray();
var unloadedNancyAssemblies =
Directory.GetFiles(containingDirectory, wildcardFilename).Where(
f => !existingAssemblyPaths.Contains(f, StringComparer.InvariantCultureIgnoreCase));
foreach (var unloadedNancyAssembly in unloadedNancyAssemblies)
{
Assembly.Load(AssemblyName.GetAssemblyName(unloadedNancyAssembly));
}
UpdateTypes();
}
/// <summary>
/// Refreshes the type cache if additional assemblies have been loaded.
/// Note: This is called automatically if assemblies are loaded using LoadAssemblies.
/// </summary>
public static void UpdateTypes()
{
UpdateAssemblies();
types = (from assembly in assemblies
from type in Nancy.Extensions.AssemblyExtensions.SafeGetExportedTypes(assembly)
where !type.IsAbstract
select type).ToArray();
}
/// <summary>
/// Updates the assembly cache from the appdomain
/// </summary>
private static void UpdateAssemblies()
{
assemblies = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
where !assembly.IsDynamic
where !assembly.ReflectionOnly
select assembly).ToArray();
}
/// <summary>
/// Loads any Nancy*.dll assemblies in the app domain base directory
/// </summary>
private static void LoadNancyAssemblies()
{
LoadAssemblies(@"Nancy*.dll");
}
}
}
namespace AppDomainTypeScanning
{
using System.Linq;
using Xunit;
public class AppDomainTypeScannerFixture
{
[Fact]
public void Should_contain_types_from_referenced_but_unused_assembly()
{
var types = AppDomainTypeScanner.Types;
var result = types.Where(t => t.AssemblyQualifiedName.Contains(@".Spark")).Any();
Assert.True(result);
}
}
}
namespace AppDomainTypeScanning
{
using System;
using System.IO;
using System.Reflection;
/// <summary>
/// Assembly extension methods
/// </summary>
public static class AssemblyExtensions
{
/// <summary>
/// Gets exported types from an assembly and catches common errors
/// that occur when running under test runners.
/// </summary>
/// <param name="assembly">Assembly to retreive from</param>
/// <returns>An array of types</returns>
public static Type[] SafeGetExportedTypes(this Assembly assembly)
{
Type[] types;
try
{
types = assembly.GetExportedTypes();
}
catch (FileNotFoundException)
{
types = new Type[] { };
}
catch (NotSupportedException)
{
types = new Type[] { };
}
return types;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment