Innanzitutto, assicurati di profilare correttamente le prestazioni. Ad esempio, eseguire la query due volte da ADO.NET e verificare se la seconda volta è molto più veloce della prima volta. Ciò elimina il sovraccarico di attesa per la compilazione dell'app e l'aumento dell'infrastruttura di debug.
Quindi, controlla le impostazioni predefinite in ADO.NET e SSMS. Ad esempio, se si esegue SET ARITHABORT OFF in SSMS, è possibile che ora sia lento come quando si utilizza ADO.NET.
Quello che ho scoperto una volta era che SET ARITHABORT OFF in SSMS causava la ricompilazione del proc memorizzato e/o l'utilizzo di statistiche diverse. E all'improvviso sia SSMS che ADO.NET riportavano all'incirca lo stesso tempo di esecuzione.
Per verificarlo, guarda i piani di esecuzione per ogni esecuzione, in particolare la tabella syscheobjects. Probabilmente saranno diversi.
L'esecuzione di "sp_recompile" su una procedura memorizzata specifica eliminerà il piano di esecuzione associato dalla cache, che quindi offre a SQL Server la possibilità di creare un piano possibilmente più appropriato alla successiva esecuzione della procedura.
Infine, puoi provare l'approccio "nuke it from orbit" per ripulire l'intera cache della procedura e i buffer di memoria utilizzando SSMS:
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
In questo modo prima di testare la query si impedisce l'utilizzo dei piani di esecuzione memorizzati nella cache e della cache dei risultati precedenti.