ASP.NET - Asincronismo

Le tecniche di asincronismo (da non scambiare con AJAX) sono delle tecniche che possono tornarci in auto sopratutto per quelle pagine che richiedono molto tempo nell'acquisizione dei dati.

La logica è quella di spostare il codice che richiede molto tempo, dal thread di ASP.NET ad un thread non ASP.NET.
La pagina verrà comunque servita all'utente con la stessa tempistica di una pagina sincrona.

Perchè l'asincronismo?
ASP.NET può gestire 20 Threads, per ogni thread può gestire una Request Queue di 1000 richieste, se non avremo più risorse riceveremo un 503 – Service Unavailable.

Quello che facciamo noi è liberare il prima possibile uno dei 20 thread durante una fase time-consuming.

Ci sono vari modi per lavorare con l'asincronismo quello che vedremo adesso è asynchronous pages.

L'ipotesi, abbastanza accreditabile, è un'applicazione ASP.NET che richiede dei dati ad un servizio WCF il quale perderà del tempo prima di rispondere.

Per prima cosa, per poter attivare l'asincronismo, dobbiamo inserire il seguente attributo nella direttiva Page della nostra pagina:

Async="true"

Adesso aggiungiamo un Web Service al nostro progetto e inseriamo del codice che richiede del tempo per esser eseguito:

        [WebMethod]
        public string GetDataWithDelay(int delay)
        {
            if (delay > 0)
                Thread.Sleep(delay * 1000);
            return "Dati di ritorno dal metodo GetDataWithDelay";
        }

Adesso aggiungiamo un Web Reference al nostro progetto, specificando il web server appena creato. Fatto questo lo utilizzeremo nostra pagina ASP.NET:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="WebApplication1._Default" Async="true" Trace="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="Label1" runat="server" Text="" />
     </div>
    </form>
</body>
</html>
        protected void Page_Load(object sender, EventArgs e)
        {
            localhost.WebService1 ws1 = new localhost.WebService1();
            ws1.GetDataCompleted += new WebApplication1.localhost.GetDataCompletedEventHandler(ws1_GetDataCompleted);
            ws1.GetDataAsync(3);
            Trace.Warn(Thread.CurrentThread.ManagedThreadId.ToString());
        }

        void ws1_GetDataCompleted(object sender, WebApplication1.localhost.GetDataCompletedEventArgs e)
        {
            Label1.Text = e.ToString();
            Trace.Warn(Thread.CurrentThread.ManagedThreadId.ToString());
        }

Testando questo codice vi verrà visualizzata una pagina con il trace abilitato.

Il primo numero rosso che vedrete, è il thread di ASP.NET utilizzato all'inizio dell'applicazione.
Come potete vedere dal vostro trace, il thread viene rilasciato immediatamente e, poco sotto, c'è un'altro numero in rosso che identifica l'altro thread di ASP.NET utilizzato nel momento in cui i dati sono disponibili, ovvero una volta dentro il metodo ws1_GetDataCompleted.

Questo renderà la vostra applicazione più scalabile e renderà difficile un errore di tipo 503 – Service Unavailable.

Tags:

ASP.NET Caching - System.Web.Caching.CacheDependency

ASP.NET ci da la possibilità di creare le nostre CacheDependecy, facendo derivare la nostra classe dalla CacheDependecy.

Grazie a questa classe abbiamo possiblità di creare una dipendenza anche per un web service da noi creato.

Il processo è alquanto semplice, bisogna creare una classe che deriva dalla CacheDependecy e far partire un processo asincrono che controllerà se le dipendenze sono cambiate. Se queste cambiano, chiameremo il metodo CacheDependency.NotifyDependencyChange() in automatico la classe CacheDependecy aggiornerà le proprietà HasChanged e UtcLastModified.

Le tecniche più usate per implementare il controllo delle risorse sono 3:

  1. Timer: un timer che effettua un poll sulle risorse
  2. Thread: un thread che controllerà le nostre risorse
  3. Event: quando l'evento viene scaturito, si controllano le risorse

Ecco un semplice esempio usando un Timer:

public class TimerTestCacheDependency : System.Web.Caching.CacheDependency                                                   
{                                                                                                                            
    private System.Threading.Timer timer;                                                                                    
    private const int pollTime = 5000;                                                                                       
    public TimerTestCacheDependency()                                                                                        
    {                                                                                                                        
        timer = new System.Threading.Timer(new System.Threading.TimerCallback(CheckDependencyCallback), this, 0, pollTime);  
    }                                                                                                                        
                                                                                                                             
    private int count = 0;                                                                                                   
    private void CheckDependencyCallback(object sender)                                                                      
    {                                                                                                                        
        count++;                                                                                                             
        if (count > 4)                                                                                                       
        {                                                                                                                    
            base.NotifyDependencyChanged(this, EventArgs.Empty);                                                             
            timer.Dispose();                                                                                                 
        }                                                                                                                    
    }                                                                                                                        
                                                                                                                             
    protected override void DependencyDispose()                                                                              
    {                                                                                                                        
        if (timer != null) timer.Dispose();                                                                                  
    }                                                                                                                        
}                                                                                                                            

L'esempio è semplicissimo.
Il timer controllerà il valore di count quando questo è maggiore a 4 verrà notificato che la risorsa è cambiata, tramite la base.NotifyDependencyChanged.

E' importante fare l'override del metodo DependencyDispose per togliere quello che più non serve una volta che l'oggetto in cache è stato invalidato.

L'utilizzo è lo stesso di tutti gli altri Dependency:

TimerTestCacheDependency dependency = new TimerTestCacheDependency();
Cache.Insert("key", item, dependency);                               

Customizzare la Dependency, può tornare molto utile con i nostri servizi WCF o servizi quali Active Directory, Microsoft Messaging Queuing (MSMQ) e tanti altri servizi.

ASP.NET Caching - Cache notification SQL Server 2005 e SQL Server 2008

SQL Server 2005 e SQL Server 2008 usano una soluzione di notifica molto simile fra loro e integrata nel database con un sistema di messaggistica chiamato Service Broker.

Service Broker si occupa di gestire la coda dei messaggi di notifica di un database.

.NET offre un modello integrato con ADO.NET in modo tale da istruire SQL Server per mandare una notifica per qualsiasi operazione che modifica il risultato di una query da noi registrata.

ASP.NET offre un modello che si integra perfettamente con il precedente e ci permette di invalidare gli oggetti in cache automaticamente.

Per questo esempio, creiamo un nuovo database (TestDB) con una tabella (Contact).

 1_sqlCache

Adesso apriamo il prompt di DOS e digitiamo:

cd %windir%\Microsoft.NET\Framework\v2.0.50727

Quì abbiamo la possibilità di lanciare il seguente comando:

aspnet_regsql.exe -E -ed -d TestDB -et -t Contact

Il quale, specificando il database e la tabella nella quale vogliamo abilitare la notifica, modificherà il nostro database così:

2_sqlCache

Adesso che il database è pronto, dobbiamo abilitare ASP.NET affinchè si occupi di fare il polling sul database.

Creiamo un nuovo progetto e aggiungiamo una label, con ID Label1, nella pagina Default.aspx. Nel codice della pagina, nel nel metodo Page_Load, inseriamo:

Label1.Text = System.DateTime.Now.ToString();

Apriamo il Server Explorer di Visual Studio 2008, create una nuova connessione (TestDBConnectionString) per il database e la tabella appena configurati e tranisciamo la tabella Contact all'interno della pagina Default.aspx.

Testando il progetto si nota che il timestamp della label cambia ad ogni refresh.

Ritorniamo al makup language della pagina Default.aspx e inseriamo la direttiva:

<%@ OutputCache Duration="3600" SqlDependency="TestDB:Contact" VaryByParam="none" %>

A differenza degli altri casi, quì specifichiamo SqlDependecy, specificando il database e la tabella interessate.
Il nome del database viene specificato nel web.config. Per questo dovremo modificare il web.config come segue:

  <system.web>
    <caching>
      <sqlCacheDependency enabled = "true" pollTime = "1000">
        <databases>
          <add name="TestDB" connectionStringName="TestDBConnectionString" pollTime = "1000" />
        </databases>
      </sqlCacheDependency>
    </caching>
  </system.web>

Il pollTime specifica il numero di millisecondi tra un controllo e l'altro (il valore minimo è 500ms).
Bisogna anche specificare la ConnectionStringName utilizzata.

Lanciate il progetto, provate ad aggiornare un paio di volte la pagina.
Come potete vedere, il timestamp nella label non verrà aggiornato.

Aprire il Server Explorer, selezionate la tabella Contact con il tasto destro e cliccate sulla voce Open Table Definition e provate a modificare i valori all'interno della tabella.

Provate a aggiornare la pagina su IE e i dati al suo interno verranno aggiornati.
Riprovate ad aggiornare la pagina e la pagina non verrà aggiornata.

Ci sono un paio di regole fondamentali da seguire quando si crea una query per Service Broker, un elenco completo potete trovarlo su SQL Server Books Online:

  1. Le query devono essere del tipo:
    [Owner].table ad esempio dbo.Contact
  2. Non si possono usare metodi quali:
    • COUNT
    • MAX
    • MIN
    • AVERAGE
  3. Bisogna specificare ogni singola colonna che ci interessa.

Tags: |