Migrazione ASP classico a ASP.NET Core: guida
Matteo Migliore

Matteo Migliore è un imprenditore e architetto software con oltre 27 anni di esperienza nello sviluppo di soluzioni basate su .NET e nell'evoluzione di architetture applicative per imprese e organizzazioni di alto profilo.

Ha guidato progetti enterprise, formato centinaia di sviluppatori e aiutato aziende di ogni dimensione a semplificare la complessità trasformando il software in guadagni per il business.

Il gestionale ordini di un'azienda di ricambi del Bergamasco è fatto di 214 file .asp, scritti fra il 2003 e il 2011, e gira su un Windows Server 2012 che nessuno ha il coraggio di spegnere. Il titolare mi ha chiesto una cosa sola: "Quanto costa portarlo su qualcosa che esista ancora fra cinque anni, senza fermare i ventidue agenti che ci lavorano ogni giorno?".

La risposta onesta ha tre numeri. Un sito ASP classico di quella taglia si porta su ASP.NET Core in circa 45-60 giorni persona, quindi fra 27.000 e 36.000 euro con una tariffa di riferimento di 600 euro al giorno. Si fa a pezzi, pagina dopo pagina, e il vecchio sito resta in piedi finché l'ultima pagina non è passata. E il costo non dipende dal numero di file, ma da tre cose: quanto codice VBScript sta dentro le pagine, quanti componenti COM ci sono e quanto è pulito il database sotto.

In questa guida trovi che cos'è ASP classico e perché si migra, i rischi di lasciarlo dov'è, quando conviene davvero e quando no, le quattro strade possibili con i costi, l'inventario da fare prima di scrivere una riga, il metodo che uso io (un proxy davanti ai due siti, pagina per pagina), la traduzione di VBScript e ADO in C#, sessioni e login, i componenti COM, il passaggio in produzione e gli errori che vedo ripetersi. In fondo c'è una regola in cinque righe per decidere.

Scrivo software dal 1999 e ho messo le mani su più siti ASP classico di quanti ne ricordi: gestionali di magazzino, portali per agenti, aree riservate di studi professionali. Qui non ci sono teorie, ci sono numeri e codice che uso. Se il tuo sito è invece in WebForms, il discorso cambia e lo trovi in migrazione da ASP.NET WebForms a Core.

Che cos'è ASP classico e perché esiste ancora?

ASP classico è la tecnologia web di Microsoft uscita nel 1996, in cui il codice VBScript sta dentro pagine .asp interpretate da IIS a ogni richiesta. Non è .NET, non è compilato e non è più sviluppato dal 2000.

Una pagina ASP classico è un file di testo in cui HTML e codice sono mescolati. Il server la legge dall'alto in basso, esegue il VBScript fra i segni <% ... %> e manda il risultato al browser. Non c'è compilazione, non ci sono classi vere, non c'è separazione fra grafica e logica. Le connessioni al database si aprono con ADO, il vecchio ADODB.Connection, e ogni funzione utile si scrive a mano in file inclusi con <!--#include file="..." -->.

Perché ce n'è ancora così tanto

Per un motivo semplice: funziona. Un sito ASP classico fatto bene nel 2005 ha vissuto vent'anni senza che nessuno lo toccasse. Costa poco da mantenere finché non si rompe, e le aziende hanno fatto la scelta più razionale: non spendere finché serve. Il problema è che "finché serve" ha una data di scadenza, e non la decidi tu.

Che cosa dice Microsoft

ASP classico è incluso in IIS come funzionalità opzionale e fa parte di Windows Server 2022 e 2025, quindi non è vietato né rimosso. Ma Microsoft non lo sviluppa e non lo aggiorna da oltre vent'anni, il motore VBScript è indicato come funzionalità da eliminare nelle versioni future di Windows e non esiste una versione a 64 bit di molti componenti COM su cui questi siti si appoggiano. Il rischio non è "domani smette di funzionare": è che un giorno cambi un pezzo intorno e l'intero sito non parta più.

Quali rischi corre un'azienda che lascia girare ASP classico?

I rischi sono quattro: sicurezza (nessuna correzione e codice senza protezioni moderne), blocco dell'infrastruttura (il sito costringe a tenere vecchi sistemi operativi), persone (sempre meno sviluppatori lo conoscono) e un costo che resta nascosto finché non si rompe qualcosa.

Sicurezza: il rischio più concreto

Nelle pagine ASP classico che ho letto, la query SQL è quasi sempre costruita incollando il testo che arriva dal browser: "SELECT * FROM Clienti WHERE Id = " & Request("id"). È la porta aperta alla SQL injection. Mancano il token anti-falsificazione dei moduli, la gestione moderna delle password, le intestazioni di sicurezza e gli strumenti che oggi un framework dà gratis. Aggiungerli a mano a 214 file è possibile, ma costa quasi come migrare.

Infrastruttura: il sito tiene in ostaggio il server

Un sito ASP classico con componenti COM a 32 bit ti obbliga a tenere IIS in una configurazione precisa. Se il server è un Windows Server 2012, il supporto di Microsoft è finito e le correzioni di sicurezza si pagano con un contratto esteso. Cambiare server non è possibile senza ripetere a mano ogni installazione di componenti, con le versioni giuste delle librerie.

Persone: nessuno lo conosce più

Uno sviluppatore che oggi ha 28 anni non ha mai scritto ASP classico. Chi lo conosce ha in genere più di 45 anni, costa di più e spesso è l'unico a sapere come funziona. Quando quella persona se ne va, l'azienda ha un sistema che nessuno sa modificare. Lo chiamo rischio della persona sola, ed è quello che fa decidere i titolari: non la tecnologia, ma un nome su un foglio. Un modo per ridurlo già oggi è lavorare in coppia: come funziona lo spiego in pair programming, cosa è e quando funziona.

Costo: il canone che non vedi

Il sito non ha un canone, ma ha un costo. Ogni modifica richiede più tempo perché non esistono test, ogni errore si scopre in produzione, ogni nuova funzione si appoggia su codice che nessuno ha il coraggio di toccare. Ne parlo con i numeri in quanto costa davvero il software legacy: per un gestionale come quello di cui parlo qui, il costo nascosto è in genere fra 6.000 e 12.000 euro l'anno.

Quando conviene migrare ASP classico e quando no?

Conviene migrare quando il sito è usato ogni giorno per lavorare, quando serve cambiarlo spesso o quando il server sta per diventare un problema. Non conviene quando il sito è usato due volte l'anno, non cambia mai e non contiene dati personali esposti a Internet.

Quando migrare

  • Il sito è esposto a Internet e contiene login, dati di clienti o ordini. Qui il rischio di sicurezza da solo giustifica il lavoro.
  • Il server è in scadenza o si vuole spostare su una macchina virtuale, su Azure o su un hosting diverso.
  • Serve cambiarlo almeno una volta al mese e ogni modifica richiede giorni invece di ore.
  • L'unica persona che lo conosce sta per andarsene o è già un consulente esterno.
  • Servono integrazioni con altri sistemi (fatture elettroniche, corrieri, pagamenti), che con ASP classico sono dolorose.

Quando aspettare

Se il sito è una intranet su una rete chiusa, usato da quattro persone per consultare un archivio che non cambia, non c'è fretta. Basta fare tre cose: una copia completa del server, una macchina virtuale di riserva e un documento che spiega come si reinstalla. Costa due giorni e compra anni. Spendere 30.000 euro per riscrivere un archivio consultato da quattro persone è un errore che vedo fare più spesso di quanto si pensi.

Il test dei tre numeri

Prima di decidere, conta tre cose: quanti file .asp ci sono, quante persone lo usano ogni giorno e quante volte l'anno lo modifichi. Se sono più di 100, più di 10 e più di 6, la migrazione si ripaga in meno di tre anni. Se sono meno di 30, meno di 5 e meno di 2, tienilo com'è e metti in sicurezza la macchina.

Quali strade esistono per migrare da ASP classico a ASP.NET Core?

Le strade sono quattro: tenere tutto e metterlo in sicurezza, migrare pagina per pagina con un proxy davanti (strangler), riscrivere tutto da capo, oppure sostituire il sito con un prodotto pronto. Per un sito usato ogni giorno quasi sempre vince la seconda.

Un proxy davanti ai due siti: le pagine già migrate vanno ad ASP.NET Core, le altre restano su ASP classico

Strada 1: tenere e mettere in sicurezza

Non si cambia il codice. Si mette il sito dietro un firewall applicativo, si chiude l'accesso diretto al database, si fa una copia della macchina e si scrive come reinstallarla. Costo: 2.000-4.000 euro una volta. Non risolve il problema, lo rimanda, ma per un sito poco importante è la scelta giusta.

Strada 2: migrare pagina per pagina (strangler)

Davanti ai due siti si mette un proxy. Le pagine già migrate vengono servite dal nuovo ASP.NET Core, tutte le altre dal vecchio ASP classico. Ogni settimana si sposta qualche pagina; gli utenti non vedono nulla perché l'indirizzo non cambia. È la tecnica che descrivo in strangler fig pattern per modernizzare il software legacy, ed è quella che consiglio nella maggioranza dei casi. Il rischio è basso perché a ogni passo il sito funziona.

Strada 3: riscrivere tutto da capo

Si costruisce un sito nuovo in parallelo e si passa in un colpo solo. Ha senso quando la logica del vecchio sito è sbagliata e va ripensata, non solo tradotta. Ha il difetto di tutti i progetti a lancio unico: per mesi si paga senza vedere nulla in produzione, e il giorno del passaggio si scoprono le regole che nessuno aveva scritto. Ne parlo in riscrivere o rifattorizzare il software legacy.

Strada 4: sostituire con un prodotto pronto

Se il sito fa cose che un gestionale commerciale fa già (ordini, magazzino, fatturazione standard), può convenire comprare. Il costo è la perdita delle regole su misura e un periodo di adattamento. Prima di scegliere, vale la pena leggere quanto costa un software su misura, per avere il confronto con la cifra giusta.

Confronto in una tabella

StradaCosto tipico (sito da 200 file)TempoRischio
Tenere e mettere in sicurezza2.000-4.000 euro2-4 giorniResta il problema
Strangler pagina per pagina27.000-36.000 euro4-6 mesi, senza fermateBasso
Riscrittura completa45.000-70.000 euro6-9 mesiAlto
Prodotto prontoda 8.000 euro l'anno2-4 mesiPerdi le regole su misura

Quanto costa migrare ASP classico a ASP.NET Core?

Un sito con circa 200 file si migra in 45-60 giorni persona, 27.000-36.000 euro a 600 euro al giorno. Un sito piccolo (40 file) costa 8.000-12.000 euro. Il costo dipende dal codice dentro le pagine, dai componenti COM e dalla pulizia del database, non dal numero di file.

Confronto dei costi delle quattro strade: sicurezza 3.000 euro, strangler 31.000, riscrittura 55.000, prodotto pronto 8.000 euro l'anno

Come si costruisce il preventivo

Non esiste un prezzo a pagina, perché una pagina con un modulo di tre campi e una pagina con un calcolo di listino a sette livelli non sono lo stesso lavoro. Io faccio un conteggio in giorni per quattro voci.

VoceChe cosa includeGiorni per un sito da 200 file
Inventario e analisiElenco di file, query, componenti, regole4-6
FondamentaProgetto ASP.NET Core, proxy, accesso ai dati, login6-8
Pagine e logicaTraduzione di pagine, calcoli, report28-38
Test, passaggio, spegnimentoConfronto dei risultati, formazione, ritiro del vecchio sito7-10

La somma fa 45-62 giorni. A 600 euro al giorno, da 27.000 a 37.000 euro. Per il gestionale ricambi del Bergamasco il conto finale è stato di 52 giorni e 31.200 euro, in cinque mesi, senza una sola ora di fermo.

Che cosa fa salire il prezzo

  • Componenti COM senza codice sorgente (una DLL per i calcoli, una per la stampa): ognuna può costare da 3 a 10 giorni.
  • Logica nel database dentro stored procedure non documentate: vanno lette, non solo spostate.
  • Pagine che scrivono file o inviano posta con CDO: vanno riprogettate.
  • Nessun ambiente di prova: se si lavora sul sito vero, ogni passo costa il doppio di prudenza.

Che cosa lo fa scendere

Un database in ordine, pagine che usano funzioni ripetute (e non copie incollate), la disponibilità di una persona interna che conosce le regole e risponde in un giorno. L'ultimo punto vale da solo il 20% del preventivo. Per capire come si valuta chi fa il lavoro, trovi i criteri in come scegliere una software house.

Se la tua situazione è più vicina a una migrazione di piattaforma che di linguaggio, confronta con migrazione da .NET Framework a .NET 10: lì il codice è già C#, e i costi sono molto più bassi.

Come si fa l'inventario prima di toccare il codice?

L'inventario risponde a quattro domande: quali file esistono, quali si usano davvero, da che cosa dipendono e quali regole di business nascondono. Si fa in 4-6 giorni, e ogni giorno speso qui ne risparmia tre dopo.

Conta i file e guarda cosa contengono

Il primo passo è meccanico. Da PowerShell, nella cartella del sito:

// Strumento da riga di comando: elenca i file .asp con righe di codice e dipendenze.
// Si compila con: dotnet new console, incolla questo in Program.cs, dotnet run -- "C:\\inetpub\\sito"
using System.Text.RegularExpressions;

var radice = args.Length > 0 ? args[0] : ".";
var dipendenze = new Regex(@"CreateObject\(\s*""([^""]+)""", RegexOptions.IgnoreCase);
var inclusioni = new Regex(@"<!--\s*#include\s+(file|virtual)\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);

foreach (var file in Directory.EnumerateFiles(radice, "*.asp", SearchOption.AllDirectories))
{
    var testo = File.ReadAllText(file);
    var righe = testo.Split('\n').Length;
    var componenti = dipendenze.Matches(testo).Select(m => m.Groups[1].Value).Distinct();
    var inclusi = inclusioni.Matches(testo).Select(m => m.Groups[2].Value).Distinct();
    Console.WriteLine($"{Path.GetRelativePath(radice, file)};{righe};{string.Join("|", componenti)};{string.Join("|", inclusi)}");
}

L'output è un elenco con una riga per file: nome, righe, componenti COM usati e file inclusi. Aperto in un foglio di calcolo, dice subito dove sta il peso. In quasi tutti i siti che ho visto, il 20% dei file contiene l'80% della logica, e un'altra buona parte sono pagine quasi vuote che includono la stessa intestazione.

Scopri cosa si usa davvero

Un sito con vent'anni di storia ha sempre pagine morte. Il modo più sicuro per saperlo è leggere i registri di IIS degli ultimi dodici mesi: le pagine che non compaiono mai non si migrano. Un'analisi sui file u_ex*.log con una query in C# o con Log Parser dà l'elenco in un quarto d'ora. Nel sito del Bergamasco, 61 pagine su 214 non erano state aperte da nessuno in un anno. Sono sparite dal preventivo.

Elenca i componenti COM

Ogni Server.CreateObject("Qualcosa.Classe") è una dipendenza. Mettili in una tabella con tre colonne: che cosa fa, se hai il sorgente e se esiste un equivalente in .NET. I più comuni sono ADODB.Connection (diventa Dapper o Entity Framework), Scripting.FileSystemObject (diventa System.IO), CDO.Message (diventa un servizio di posta), MSXML2.ServerXMLHTTP (diventa HttpClient) e le DLL proprietarie, che sono il problema vero.

Scrivi le regole che esistono solo nel codice

Il codice ASP spesso contiene regole che nessuno ricorda: "se il cliente è di categoria C e l'ordine supera 5.000 euro, applica il 3% e manda una mail al responsabile". Vanno estratte e scritte in un documento di una pagina, perché saranno la base dei test di confronto. Le regole non scritte sono la prima causa di sorprese il giorno del passaggio.

Come si migra pagina per pagina con un proxy davanti?

Si crea un progetto ASP.NET Core che fa da ingresso unico con YARP, il proxy inverso di Microsoft. Le pagine migrate le serve il nuovo codice, tutte le altre vengono inoltrate al vecchio sito. Gli utenti non cambiano indirizzo e non si accorgono di nulla.

Il progetto di partenza

Si parte da un progetto vuoto e si aggiunge YARP:

// dotnet new web -n NuovoSito
// dotnet add package Yarp.ReverseProxy
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

// Le pagine già migrate hanno una rotta propria e vincono sul proxy.
app.MapGet("/clienti/elenco", () => Results.Content("<h1>Elenco clienti (nuovo)</h1>", "text/html"));

// Tutto il resto va al vecchio sito ASP classico.
app.MapReverseProxy();

app.Run();

E la configurazione in appsettings.json, che dice dove abita il vecchio sito:

{
  "ReverseProxy": {
    "Routes": {
      "vecchio-sito": {
        "ClusterId": "asp-classico",
        "Match": { "Path": "{**catch-all}" }
      }
    },
    "Clusters": {
      "asp-classico": {
        "Destinations": {
          "principale": { "Address": "http://localhost:8080/" }
        }
      }
    }
  }
}

Il vecchio sito si sposta su una porta interna (qui la 8080) e non è più raggiungibile dall'esterno. Il nuovo ASP.NET Core ascolta sulle porte 80 e 443 e inoltra tutto quello che non conosce. Il giorno zero non cambia nulla per nessuno, e questo è il punto: il rischio al primo passo è praticamente nullo.

Il ritmo di lavoro

Ogni settimana si sceglie un gruppo di pagine, di solito una funzione completa (l'elenco clienti con la scheda e la modifica). Si scrive la versione nuova, la si confronta con la vecchia sugli stessi dati, e solo quando i risultati coincidono si aggiunge la rotta. Se qualcosa va storto, si toglie la rotta e il traffico torna al vecchio sito in un secondo. È un rollback gratuito, e vale da solo il prezzo del metodo.

Quali pagine per prime

Le ultime a cadere sono le più complesse, le prime sono quelle di sola lettura: elenchi, schede, report. Sono semplici, si confrontano bene e danno subito agli utenti qualcosa di nuovo, più veloce e adatto al telefono. Il consenso del team, a questo punto, è già conquistato per le fasi difficili.

Come si traduce VBScript con ADO in C# moderno?

Ogni blocco VBScript si traduce con una regola fissa: ADO diventa Dapper con query parametrizzate, i Recordset diventano liste di oggetti tipizzati e i Response.Write diventano viste Razor. La parte difficile non è la sintassi, ma le regole nascoste.

Un esempio vero, prima e dopo

Questo è il tipo di codice che trovo in quasi tutti i siti. Una pagina che mostra gli ordini di un cliente:

' ASP classico (VBScript) - NON usare: vulnerabile a SQL injection
Dim conn, rs, sql
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open Application("ConnString")
sql = "SELECT Id, Data, Totale FROM Ordini WHERE IdCliente = " & Request("id") & " ORDER BY Data DESC"
Set rs = conn.Execute(sql)
Do While Not rs.EOF
    Response.Write "<tr><td>" & rs("Data") & "</td><td>" & rs("Totale") & "</td></tr>"
    rs.MoveNext
Loop
rs.Close
conn.Close

Qui ci sono tre problemi in dieci righe: la query incolla l'identificatore dalla richiesta (SQL injection), il testo restituito non è protetto (XSS) e non c'è gestione degli errori. In C# con Dapper diventa:

// dotnet add package Dapper
// dotnet add package Microsoft.Data.SqlClient
using Dapper;
using Microsoft.Data.SqlClient;

public record OrdineRiga(int Id, DateTime Data, decimal Totale);

public class OrdiniRepository(IConfiguration configurazione)
{
    private readonly string _connessione = configurazione.GetConnectionString("Gestionale")
        ?? throw new InvalidOperationException("Stringa di connessione mancante.");

    public async Task<IReadOnlyList<OrdineRiga>> PerClienteAsync(int idCliente)
    {
        const string sql = """
            SELECT Id, Data, Totale
            FROM dbo.Ordini
            WHERE IdCliente = @idCliente
            ORDER BY Data DESC
            """;

        await using var conn = new SqlConnection(_connessione);
        var righe = await conn.QueryAsync<OrdineRiga>(sql, new { idCliente });
        return righe.AsList();
    }
}

Il parametro @idCliente è tipizzato come intero: se dal browser arriva testo, la richiesta fallisce prima di toccare il database. La stessa pagina diventa un endpoint che restituisce l'elenco, e una vista Razor che lo mostra con la codifica automatica del testo, quindi senza XSS. Per decidere fra Dapper ed Entity Framework nel tuo caso, c'è il confronto in Dapper contro Entity Framework.

Le trappole di traduzione che vedo sempre

  • Le date: VBScript usa il formato del server, e molte pagine fanno confronti su stringhe. In C# si usano DateTime e parametri, mai testo.
  • I valori nulli: Null, stringa vuota e zero in VBScript si confondono con facilità. Ogni If rs("campo") = "" va riletto per capire che cosa voleva dire davvero.
  • Le variabili globali: nelle pagine ASP una variabile dichiarata in un file incluso è visibile ovunque. In C# diventa un parametro, e si scopre quanto codice dipendeva da effetti nascosti.
  • Il tipo variante: Dim x senza tipo contiene a volte un numero, a volte un testo. Il compilatore C# ti obbliga a decidere, ed è un bene.

Si traduce a mano o con uno strumento?

Esistono convertitori automatici da VBScript a C#, ma producono codice che compila e non è leggibile. Io uso un assistente AI per la prima bozza di ogni pagina, poi riscrivo e collaudo a mano. Su una pagina media di 150 righe, la bozza fa risparmiare circa metà del tempo di scrittura; il tempo di collaudo non cambia. Chi promette che l'AI converte il sito da sola sta vendendo una cosa che non esiste.

Come si gestiscono sessioni, login e permessi?

Si sostituisce Session("utente") con l'autenticazione a cookie di ASP.NET Core e si leggono i permessi da claim e ruoli. Per non far fare un secondo login agli utenti durante la migrazione, il nuovo sito legge lo stesso cookie del vecchio oppure si sposta il login per primo.

Il problema del doppio login

Nel sito ASP classico l'utente è nella sessione del server (Session("idUtente")), che vive in memoria nel processo di IIS. Il nuovo sito è un altro processo e non vede quella sessione. Se non fai nulla, l'utente entra nel vecchio sito, clicca su una pagina migrata e gli viene chiesto di nuovo il login. È il primo motivo di lamentele in un progetto a pagine alternate.

La soluzione più semplice: il login passa per primo

Si migra per prima la pagina di accesso. Il nuovo sito crea il cookie di autenticazione, e quando inoltra una richiesta al vecchio sito aggiunge un'intestazione con l'utente. Il vecchio sito si fida di quell'intestazione, perché ormai la porta 8080 è raggiungibile solo dal proxy, e la mette nella sua sessione con poche righe in un file incluso. Il codice del nuovo sito:

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(opzioni =>
    {
        opzioni.LoginPath = "/accesso";
        opzioni.ExpireTimeSpan = TimeSpan.FromHours(8);
        opzioni.SlidingExpiration = true;
        opzioni.Cookie.HttpOnly = true;
        opzioni.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    });
builder.Services.AddAuthorization();
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(contesto =>
    {
        // Passa l'utente al vecchio sito, che si fida solo del proxy.
        contesto.AddRequestTransform(async transform =>
        {
            transform.ProxyRequest.Headers.Remove("X-Utente");
            var utente = transform.HttpContext.User;
            if (utente.Identity?.IsAuthenticated == true)
            {
                var id = utente.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
                transform.ProxyRequest.Headers.Add("X-Utente", id);
            }
            await Task.CompletedTask;
        });
    });

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

app.MapPost("/accesso", async (HttpContext http, string email, string password) =>
{
    // Qui: verifica con l'hash della password (vedi sotto), mai in chiaro.
    var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, "42"), new(ClaimTypes.Name, email), new(ClaimTypes.Role, "Agente") };
    var identita = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    await http.SignInAsync(new ClaimsPrincipal(identita));
    return Results.Redirect("/");
});

app.MapReverseProxy().RequireAuthorization();
app.Run();

Nota la riga che rimuove X-Utente dalla richiesta in arrivo: senza di essa, un utente malintenzionato potrebbe inviarla da solo e fingersi un altro. Il vecchio sito, a sua volta, deve rifiutare ogni richiesta che non arrivi dal proxy, con una regola di IIS sull'indirizzo di provenienza.

Le password: l'altra metà del lavoro

In molti siti ASP classico le password stanno nel database in chiaro o con un hash debole (MD5 senza sale). Non si trasferiscono come sono. Il modo che uso: al primo accesso riuscito con il vecchio hash, si ricalcola la password con PasswordHasher di ASP.NET Core e si salva il nuovo valore. Dopo qualche mese, chi non è mai entrato viene invitato a reimpostarla con un link via posta. Nessun utente deve cambiare password di colpo, e il database smette di contenere dati rubabili.

I permessi

Nel codice vecchio i permessi sono spesso If Session("livello") > 2 Then sparsi in cento pagine. Nel nuovo sito diventano ruoli e criteri dichiarati una volta sola ([Authorize(Roles = "Amministrazione")]). Il lavoro vero è elencare i livelli esistenti e capire cosa significano: nel gestionale ricambi, il livello 3 voleva dire "vede i prezzi d'acquisto" e il 4 "può annullare ordini", e nessuno lo aveva scritto da nessuna parte.

Che cosa succede ai componenti COM e al database?

I componenti COM con il sorgente si riscrivono in C#, quelli senza sorgente si richiamano con un piccolo servizio a 32 bit finché non si riesce a sostituirli. Il database di solito resta com'è: si cambia il codice che lo usa, non i dati.

Componenti COM con il sorgente

Se hai il codice (spesso Visual Basic 6 o Delphi), conviene riscriverlo come libreria di classi in C#. Il tempo dipende dalla logica: una DLL che calcola un listino con venti regole richiede 3-5 giorni, compresi i test di confronto. Il punto di forza è che il comportamento è definito e si può confrontare ingresso per ingresso. Se il codice sorgente è VB6, trovi il percorso in migrazione da VB6 a .NET; per Delphi c'è migrazione da Delphi a .NET.

Componenti COM senza sorgente

È il caso più scomodo. La DLL esiste, funziona e nessuno sa com'è fatta. Le opzioni sono due: ricostruire il comportamento osservando ingressi e uscite (si scrive un test per ogni caso e si riscrive il calcolo), oppure tenerla in un piccolo programma a 32 bit che il nuovo sito chiama via HTTP. La seconda è una soluzione provvisoria: tiene vivo il rischio, ma lo isola in un posto solo e toglie la fretta.

Il database sotto

Nella maggior parte dei casi il database è SQL Server (a volte Access, a volte MySQL) e non cambia. Si controllano tre cose: che la versione sia ancora supportata, che esistano gli indici sulle colonne usate nelle ricerche e che le query non facciano SELECT * su tabelle grandi. Se i dati stanno ancora in un file Access, la strada è prima spostarli su SQL Server, come spiego in migrazione di un database Access a SQL Server, poi migrare il sito. Fare le due cose insieme raddoppia il rischio.

Cose che il vecchio sito fa "di nascosto"

Ogni sito ASP classico fa qualcosa che non sta nelle pagine: un'attività pianificata di Windows che invia le fatture ogni notte, uno script che pulisce una cartella, una pagina chiamata da un altro programma. Prima di spegnere il vecchio server, controlla le attività pianificate e chiedi in giro chi chiama quali indirizzi. Nel progetto del Bergamasco abbiamo trovato una pagina sync.asp chiamata ogni cinque minuti dal programma di magazzino: se l'avessimo spenta, le giacenze si sarebbero fermate in silenzio.

Come si passa in produzione senza fermare l'azienda?

Con il metodo a pagine non esiste un giorno del passaggio: ogni pagina migrata va in produzione da sola, dopo un confronto sugli stessi dati. Il vecchio sito si spegne solo quando il registro del proxy mostra zero richieste verso di lui per quattro settimane.

Il confronto prima di ogni rotta

Per ogni pagina si scrive un confronto: gli stessi cinquanta casi, passati sia al vecchio sia al nuovo codice, con i risultati messi a fianco. Per un calcolo di listino basta un test automatico. Per una pagina di report si confrontano i totali. Il confronto non deve essere perfetto al carattere, deve essere uguale nei numeri che contano. Quando coincide, si aggiunge la rotta. Quando non coincide, quasi sempre si scopre una regola nascosta, ed è un bene scoprirla qui e non in produzione.

Una settimana tipo

  1. Lunedì: si sceglie la funzione e si leggono le pagine vecchie con chi le usa.
  2. Martedì e mercoledì: si scrive la versione nuova e i test di confronto.
  3. Giovedì: prova con due utenti fidati, sugli stessi dati.
  4. Venerdì mattina: si aggiunge la rotta. Mai il venerdì pomeriggio.
  5. Lunedì dopo: si guarda il registro degli errori e si raccolgono le segnalazioni.

Il piano di ritorno

Il ritorno è togliere una riga dal file di configurazione e riavviare l'applicazione: un minuto. Va provato prima, non dopo. Ogni rotta ha una nota con la riga da togliere e il nome di chi può farlo. Quando un agente chiama alle otto e dieci di mattina perché "la scheda cliente è strana", non è il momento di cercare come si fa.

Quando si spegne il vecchio sito

Il registro del proxy dice quante richieste vanno ancora al vecchio sito. Quando sono zero da quattro settimane, comprese la chiusura di mese e quella di anno se cadono nel periodo, si spegne. Non si cancella: si archivia la macchina virtuale per dodici mesi, con una nota su come riavviarla. Costa pochi euro l'anno e toglie l'ansia a tutti.

Formazione e comunicazione

Gli utenti non devono imparare niente di nuovo se le pagine sono uguali. Quando cambia qualcosa (un pulsante spostato, un campo reso obbligatorio), basta una riga nella pagina del giorno, scritta da chi la usa. Il metodo funziona perché il cambiamento è piccolo e continuo, e nessuno lo vive come una rivoluzione.

Quali errori vedo ripetersi nelle migrazioni da ASP classico?

I cinque errori più comuni: riscrivere tutto in un colpo solo, tradurre il codice riga per riga senza capire le regole, dimenticare le attività nascoste, rifare grafica e logica insieme, e non misurare il risultato prima e dopo.

1. Riscrivere tutto in un colpo solo

È la tentazione più forte, perché sembra più pulito. In pratica significa sei mesi senza nulla di nuovo in produzione e un giorno del passaggio pieno di sorprese. Con il metodo a pagine, la prima cosa nuova va in produzione alla terza settimana.

2. Tradurre riga per riga

Se si traduce VBScript in C# con la stessa struttura, si ottiene un programma lento da mantenere in un linguaggio nuovo: tutti i difetti di prima, con una sintassi diversa. La traduzione deve passare per il capire che cosa fa la pagina, e poi per scriverla come si scrive oggi.

3. Dimenticare le attività nascoste

Le attività pianificate, le pagine chiamate da altri programmi, le cartelle di scambio con i corrieri. Ogni vecchio sito ne ha almeno due. Si trovano guardando i registri di IIS per le chiamate che non arrivano da browser, e le attività pianificate del server.

4. Rifare grafica e logica insieme

Cambiare tutto contemporaneamente rende impossibile capire se un risultato è sbagliato per la logica o per la nuova schermata. Prima si migra a parità di aspetto, poi si rinnova la grafica, una funzione alla volta. Gli utenti ringraziano per la prima cosa e protestano per la seconda se fatta in fretta.

5. Non misurare

Prima di cominciare, annota tre numeri: il tempo medio di risposta delle pagine principali, il numero di errori al mese e i giorni che servono per una modifica semplice. Dopo la migrazione li rimisuri. Nel gestionale ricambi, il tempo di caricamento dell'elenco ordini è passato da 4,8 secondi a 0,6, e una modifica semplice da 2 giorni a 3 ore. Sono i numeri che giustificano la spesa davanti a chi firma.

Un'ultima nota sull'intelligenza artificiale

Gli assistenti AI aiutano molto in questo lavoro: leggono pagine di VBScript, spiegano che cosa fanno e propongono una bozza in C#. Ma non conoscono le regole della tua azienda e non sanno quali pagine contano. Usati bene, riducono del 30-40% i giorni di scrittura; usati male, producono codice che sembra giusto e nasconde errori sottili sui valori nulli e sulle date. La regola è semplice: l'AI scrive la bozza, un essere umano scrive il test, il test decide.

Dove va ospitato il nuovo sito dopo la migrazione?

ASP.NET Core gira su Windows, Linux e nei container. Per un gestionale aziendale le scelte realistiche sono tre: lo stesso server Windows con IIS, una macchina virtuale in sede o nel cloud, oppure un servizio gestito come Azure App Service.

Lo stesso server, con IIS

È la strada più semplice per chi ha già un server in regola. ASP.NET Core si installa su IIS con il modulo di hosting e convive con altri siti. Costo aggiuntivo: zero. Il limite è che il server resta un punto unico di guasto, e che se è un Windows Server 2012 il problema dell'infrastruttura non si è risolto, si è solo spostato.

Una macchina virtuale pulita

La scelta che consiglio più spesso: una macchina virtuale nuova, con un sistema operativo supportato, su un hosting europeo o su Azure. Il costo tipico per un gestionale con venti utenti è fra 40 e 90 euro al mese. Si guadagna il ripristino rapido (una copia della macchina si rimette in piedi in un'ora) e si può cambiare dimensione quando serve.

Un servizio gestito

Azure App Service toglie la gestione del sistema operativo. Per un sito con database SQL Server, il costo complessivo parte da circa 80 euro al mese. Ha senso se in azienda non c'è nessuno che sappia tenere un server, ed è la scelta che fa dormire più tranquilli i titolari. L'unica cosa da controllare è dove stanno i dati: per i dati dei clienti europei si sceglie un'area dell'Unione Europea.

Qualunque sia la scelta, il vantaggio dell'approccio a pagine è che puoi decidere l'hosting alla fine, non all'inizio. Il proxy gira ovunque, e spostarlo è un'operazione da una giornata.

Come decidere in cinque righe se migrare ASP classico?

Se il sito è usato ogni giorno, è esposto a Internet e lo modifichi più di sei volte l'anno, migra a pagine con un proxy. Se è una piccola intranet ferma, mettilo in sicurezza e rimanda.

  1. Conta i file .asp che si usano davvero (dai registri di IIS dell'ultimo anno).
  2. Conta gli utenti giornalieri e le modifiche all'anno.
  3. Elenca i componenti COM e chiedi se hai i sorgenti.
  4. Controlla le attività pianificate e le pagine chiamate da altri programmi.
  5. Preventiva 45-60 giorni persona ogni 200 file, e riduci per ogni cosa in ordine (database pulito, persona interna disponibile, ambiente di prova).

Con quei cinque numeri, in mezz'ora ti dico quale strada conviene e quanto costa, senza sorprese. Il lavoro vero comincia dopo, ma almeno cominci sapendo dove arrivi.

Che cosa cambia dopo la migrazione?

Cambia che il sito diventa un progetto normale: compilato, con test, versionato in Git, aggiornabile con un deploy automatico e mantenibile da qualsiasi sviluppatore .NET. Il vecchio server si spegne e il costo nascosto sparisce.

Il primo effetto che i titolari notano è la velocità: le pagine si aprono in una frazione del tempo, perché ASP.NET Core è compilato e non interpretato. Il secondo è la sicurezza: protezioni contro l'iniezione di codice, cookie sicuri e codifica del testo sono di serie. Il terzo è che le modifiche costano meno: una funzione nuova si scrive in un giorno invece che in una settimana, perché esistono test che dicono subito se qualcosa si è rotto.

Il quarto effetto è meno visibile e il più importante: il sito smette di essere un segreto di una persona. Un altro sviluppatore .NET lo apre, lo capisce in pochi giorni e lo modifica. Anche i testi e le pagine di contenuto possono uscire dal codice con un CMS headless, così chi scrive non deve aspettare uno sviluppatore. È la fine del rischio della persona sola, e per molti titolari vale più della velocità.

Se ti interessa il quadro completo di come affronto questi progetti, dall'inventario al passaggio, lo descrivo nella pagina sulla modernizzazione del software legacy. Non è un prodotto da comprare: è un metodo, e comincia sempre da quei cinque numeri.

ASP classico ha dato lavoro a un'intera generazione di aziende, e molti di quei siti hanno fatto il loro dovere per vent'anni. Spegnerli bene, senza fermare nessuno, è il modo giusto di ringraziarli.

Domande frequenti

Un sito con circa 200 file si migra in 45-60 giorni persona, 27.000-36.000 euro con una tariffa di riferimento di 600 euro al giorno. Un sito piccolo da 40 file costa 8.000-12.000 euro. Il costo dipende da codice nelle pagine, componenti COM e database più che dal numero di file.

Sì, è una funzionalità opzionale di IIS. Ma Microsoft non lo sviluppa dal 2000 e VBScript è indicato come funzionalità da eliminare in versioni future di Windows, quindi il rischio cresce ogni anno.

Sì, con un proxy come YARP davanti ai due siti. Le pagine migrate vanno al nuovo ASP.NET Core, le altre al vecchio. Il ritorno indietro è togliere una rotta, in un secondo.

Si ricostruisce il comportamento con test di ingressi e uscite, oppure si isola la DLL in un piccolo servizio a 32 bit chiamato via HTTP, come soluzione provvisoria.

No, se si migra per prima la pagina di accesso: il nuovo sito crea il cookie e passa l'utente al vecchio tramite un'intestazione che solo il proxy può inviare.

Per la prima bozza sì: fa risparmiare circa metà del tempo di scrittura. Il collaudo resta umano, perché valori nulli, date e regole nascoste sono i punti dove sbaglia in silenzio.

Quando il sito è una piccola intranet usata da poche persone, ferma da anni. Bastano una copia della macchina, una macchina virtuale di riserva e un documento di reinstallazione: due giorni di lavoro.

Lascia i tuoi dati nel form qui sotto

Matteo Migliore

Matteo Migliore è un imprenditore e architetto software con oltre 27 anni di esperienza nello sviluppo di soluzioni basate su .NET e nell'evoluzione di architetture applicative per imprese e organizzazioni di alto profilo.

Nel corso della sua carriera ha collaborato con realtà come Cotonella, Il Sole 24 Ore, FIAT e NATO, guidando team nello sviluppo di piattaforme scalabili e modernizzando ecosistemi legacy complessi.

Ha formato centinaia di sviluppatori e affiancato aziende di ogni dimensione nel trasformare il software in un vantaggio competitivo, riducendo il debito tecnico e portando risultati concreti in tempi misurabili.

Stai leggendo perché vuoi smettere di rattoppare software fragile.Scopri il metodo per progettare sistemi che reggono nel tempo.