Solitamente, quando dobbiamo eseguire codice di inizializzazione per un'applicazione ASP.NET, siamo abituati a sfruttare il metodo Application_Start contenuto all'interno di global.asax, che però risulta poco pratico nel momento in cui ci sia la necessità di gestire dipendenze da altri assembly, ognuno con la propria procedura di startup.
Immaginiamo, ad esempio, il caso in cui stiamo sfruttando Unity come container di Inversion of Control, esponendo i servizi applicativi tramite interfacce la cui implementazione concreta si trova in uno o più assembly che la nostra applicazione referenzia. Tipicamente, per configurare il nostro sistema abbiamo a disposizione due strade:
- Utilizzare un approccio centralizzato e basato su XML, ad esempio sfruttando il file di configurazione web.config, ma perdendo però ogni tipo di supporto da parte del designer di Visual Studio;
- Fare in modo che ogni assembly sia in grado di registrare autonomamente i propri oggetti sul container.
Il vantaggio della prima soluzione è quello di poter intervenire sulla configurazione a caldo, senza cioè essere costretti a ricompilare l'applicazione web. Il secondo approccio, invece, è sicuramente più strutturato, e può essere implementato piuttosto facilmente, ad esempio realizzando all'interno di ogni assembly un metodo statico simile al seguente.
public class Initializer { public static void Initialize() { ServiceLocator.Container .RegisterType<ICustomerRepository, CustomerRepository>(); // ... registrazione di altri servizi } }
Lo svantaggio risiede però nel fatto che siamo comunque costretti a invocare questi metodi all'interno di Application_Start, come nell'esempio seguente, con la necessità di effettuare una ricompilazione dell'applicazione nel caso le dipendenze varino. Inoltre, l'inizializzazione deve essere eseguita prima di ogni altro codice di Application_Start, perché quest'ultimo potrebbe potenzialmente utilizzare il container e quindi richiedere che sia completamente configurato.
protected void Application_Start() { Module1.Initialize(); Module2.Initialize(); // altri moduli da inizializzare // ... // qui codice che sfrutta i moduli inizializzati }
Nel caso di un'applicazione ASP.NET 4.0, tuttavia, esiste una terza soluzione, che presenta i vantaggi di entrambe le alternative che abbiamo presentato, e che si basa sull'utilizzo dell'attributo PreApplicationStartMethod. Tramite esso, possiamo specificare un metodo, all'interno di un assembly, che verrà automaticamente eseguito dal runtime di ASP.NET prima della fase di startup, e quindi prima anche del metodo Application_Start.
[assembly: PreApplicationStartMethod(typeof(Initializer), "Initialize")] public class Initializer { public static void Initialize() { ServiceLocator.Container .RegisterType<ICustomerRepository, CustomerRepository>(); } }
Il metodo in questione deve essere statico e privo di parametri. L'aspetto interessante è che questo attributo viene automaticamente individuato tra tutti gli assembly presenti nella cartella \bin, anche se non referenziati dal progetto ASP.NET. Il risultato, quindi, è che se ogni class library possiede il proprio metodo di inizializzazione, di fatto possiamo riconfigurare le dipendenze semplicemente copiando i relativi assembly all'interno della cartella dell'applicazione.
Non è questo l'unico scopo dell'attributo PreApplicationStartMethod: la sua natura, ad esempio, lo rende un'ottima soluzione per configurare a runtime HttpModule, BuildProvider o aggiungere riferimenti a runtime.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Evitare (o ridurre) il repo-jacking sulle GitHub Actions
Hosting di componenti WebAssembly in un'applicazione Blazor static
Ordinare randomicamente una lista in C#
Eseguire operazioni sui blob con Azure Storage Actions
Utilizzare Model as a Service su Microsoft Azure
Routing statico e PreRendering in una Blazor Web App
Aggiungere interattività lato server in Blazor 8
Usare un KeyedService di default in ASP.NET Core 8
Triggerare una pipeline su un altro repository di Azure DevOps
Sfruttare i KeyedService in un'applicazione Blazor in .NET 8
Creare un webhook in Azure DevOps
Come migrare da una form non tipizzata a una form tipizzata in Angular