Chargement dynamique des controlleurs dans une Web API
Un petit post rapide pour vous faire part d’un truc qui peut s’avérer bien pratique lorsque vous développez des Web APIs avec ASP.NET.
De base, les controlleurs de votre Web API seront chargés depuis le dossier d’exécution de celle-ci (“bin”) mais il est possible de créer votre propres AssemblyResolver:
public class ExternalFoldersAssembliesResolver : DefaultAssembliesResolver { public List<string> WatchedDirectories { get; set; } public override ICollection<Assembly> GetAssemblies() { var assemblies = new List<Assembly>(base.GetAssemblies()); if (this.WatchedDirectories.Any()) { foreach (var directory in this.WatchedDirectories) { var controllerLibrariesFiles = Directory.GetFiles(directory, "*.dll", SearchOption.TopDirectoryOnly); if (controllerLibrariesFiles.Any()) { foreach (var controllerLibrary in controllerLibrariesFiles) { try { var controllerLibraryAssembly = Assembly.LoadFrom(controllerLibrary); assemblies.Add(controllerLibraryAssembly); } catch { // We ignore errors and just continue } } } } } return assemblies; } }
La classe ci-dessus se charge de récupérer une liste de répertoire et de les parcourir pour charger l’ensemble des librairies qu’ils contiennent. Pour utiliser cette classe, rien de plus simple: sur votre objet HttpConfiguration, il suffit d’accéder à la propriété Services (qui représente le container pour l’ensemble des services par défaut) puis de remplacer le type mappé à l’interface IAssembliesResolver:
var externalFoldersAssembliesResolver = new ExternalFoldersAssembliesResolver(); externalFoldersAssembliesResolver.WatchedDirectories = new List<string> { @"C:\Temp\DLLs" }; config.Services.Replace(typeof(IAssembliesResolver), externalFoldersAssembliesResolver);
Lors de son exécution, le moteur va alors charger l’ensemble des DLLs présente dans le répertoire “C:\Tem\DLLs” (sans se soucier de savoir si les DLLs en question sont des ApiController ou non) et, si un controlleur est disponible, il sera exposé et vos utilisateur pourront y accéder!
Ainsi, le code suivant:
[RoutePrefix("api/v1/second")] public class SecondValuesController : ApiController { [Route("values")] public IEnumerable<string> Get() { return new string[] { "value5", "value6" }; } }
Bien que présent dans une autre assembly:
Est quand même appellable lorsqu’il est exposé:
Bien sur, il s’agit là d’une version “simplifiée” (naïve) du chargement dynamique: il est possible d’aller encore plus loin (détection du nom/de la classe de base de l’API, etc.) en créant une classe héritant de DefaultHttpControllerTypeResolver (ou en implémentant directement l’interface IHttpControllerTypeResolver).
Happy coding!
Commentaires