MSDN Tips & Tricks

I consigli degli esperti italiani per sfruttare al meglio gli strumenti di sviluppo e semplificare l’attività quotidiana.

In questa pagina

ASP.NET Ajax - Abilitare un Web Service per le chiamate da client script ASP.NET Ajax - Abilitare un Web Service per le chiamate da client script
Cache con dipendenza da SQL Server 2005 e ASP.NET 2.0 Cache con dipendenza da SQL Server 2005 e ASP.NET 2.0
Forzare il refresh della pagina nel browser Forzare il refresh della pagina nel browser
Salvare il contenuto di una tabella di un database in formato XML con ASP.NET 2.0 Salvare il contenuto di una tabella di un database in formato XML con ASP.NET 2.0

ASP.NET Ajax - Abilitare un Web Service per le chiamate da client script

Di Davide Vernole - Microsoft MVP

Le funzionalità di ASP.NET AJAX 1.0 permettono allo sviluppatore web, che utilizza questa tecnologia, di consumare servizi web. Se l’implementazione si basa su script lato client, è necessario decorare il Web Service con un particolare attributo al fine di garantire che il servizio sia utilizzabile. L’attributo da utilizzare a tale scopo è ScriptService come di seguito indicato:

[WebService(Namespace = "http://knodev.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    [ScriptService]
    public class AjaxEnabledWS : System.Web.Services.WebService
    {

        [WebMethod]
        public string Hello(string message)
        {
            return String.Format("Hello {0}", message);
        }
    }

Come facciamo a verificare se un Web Service è abilitato per questo tipo di chiamate? Semplice, basterà digitare, dal nostro browser, l’url del servizio seguito da /js (per esempio: https://localhost/AjaxEnabledWS.asmx/js). Se il servizio non è abilitato, otterremo il seguente errore:

Server Error in '/' Application. 
________________________________________
Only Web services with a [ScriptService] attribute on the class definition can be called from script. 

Description: An unhandled exception occurred during the execution of the current web request.
 Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Only Web services with a [ScriptService]
 attribute on the class definition can be called from script.

Source Error: 
An unhandled exception was generated during the execution of the current web request.
Information regarding the origin and location of the exception can be identified
using the exception stack trace below. 

Stack Trace: 

[InvalidOperationException: Only Web services with a [ScriptService]
attribute on the class definition can be called from script.]

   System.Web.Script.Services.WebServiceData..ctor(Type type, Boolean pageMethods) +154
   System.Web.Script.Services.WebServiceData.GetWebServiceData(HttpContext context,
String virtualPath, Boolean failIfNoData, Boolean pageMethods) +235

   System.Web.Script.Services.WebServiceClientProxyGenerator.GetClientProxyScript(HttpContext context) +49
   System.Web.Script.Services.RestClientProxyHandler.ProcessRequest(HttpContext context) +14

   System.Web.Script.Services.HandlerWrapper.ProcessRequest(HttpContext context) +10

   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +303

   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +64

________________________________________
Version Information: Microsoft .NET Framework Version:2.0.50727.312; ASP.NET Version:2.0.50727.312

Diversamente, ci sarà proposto di salvare un file che rappresenta il proxy client per le chiamate al Web Service. Quello che segue, è un esempio del contenuto del file che si ottiene se il servizio è correttamente predisposto per le chiamate client script:

Type.registerNamespace('WebLive');
WebLive.AjaxEnabledWS=function() {
WebLive.AjaxEnabledWS.initializeBase(this);
this._timeout = 0;
this._userContext = null;
this._succeeded = null;
this._failed = null;
}

WebLive.AjaxEnabledWS.prototype={
Hello:function(message,succeededCallback, failedCallback, userContext) 
{
return this._invoke(WebLive.AjaxEnabledWS.get_path(), 
'Hello',false,{message:message},succeededCallback,failedCallback,userContext);
 }}
WebLive.AjaxEnabledWS.registerClass('WebLive.AjaxEnabledWS',Sys.Net.WebServiceProxy);

WebLive.AjaxEnabledWS._staticInstance = new WebLive.AjaxEnabledWS();
WebLive.AjaxEnabledWS.set_path = function(value) 
{ WebLive.AjaxEnabledWS._staticInstance._path = value; }

WebLive.AjaxEnabledWS.get_path = function() 
{ return WebLive.AjaxEnabledWS._staticInstance._path; }

WebLive.AjaxEnabledWS.set_timeout = function(value) 
{ WebLive.AjaxEnabledWS._staticInstance._timeout = value; }
WebLive.AjaxEnabledWS.get_timeout = function() { return WebLive.AjaxEnabledWS._staticInstance._timeout; }
WebLive.AjaxEnabledWS.set_defaultUserContext = function(value)
{ WebLive.AjaxEnabledWS._staticInstance._userContext = value; }

WebLive.AjaxEnabledWS.get_defaultUserContext = function()
 { return WebLive.AjaxEnabledWS._staticInstance._userContext; }

WebLive.AjaxEnabledWS.set_defaultSucceededCallback = function(value) 
{ WebLive.AjaxEnabledWS._staticInstance._succeeded = value; }
WebLive.AjaxEnabledWS.get_defaultSucceededCallback = function()
{ return WebLive.AjaxEnabledWS._staticInstance._succeeded; }

WebLive.AjaxEnabledWS.set_defaultFailedCallback = function(value)
 
{ WebLive.AjaxEnabledWS._staticInstance._failed = value; }
WebLive.AjaxEnabledWS.get_defaultFailedCallback = function() 
{ return WebLive.AjaxEnabledWS._staticInstance._failed; }
WebLive.AjaxEnabledWS.set_path("/AjaxEnabledWS.asmx");
WebLive.AjaxEnabledWS.Hello= function(message,onSuccess,onFailed,userContext) 
{WebLive.AjaxEnabledWS._staticInstance.Hello(message,onSuccess,onFailed,userContext);
 }

In questo modo siamo pronti a utilizzare il nostro servizio da un qualsiasi script della nostra applicazione basata su ASP.NET AJAX 1.0.

 

Cache con dipendenza da SQL Server 2005 e ASP.NET 2.0

Di Daniele Bochicchio - Microsoft MVP

SQL Server 2005 offre un supporto nativo alla dipendenza su database di oggetti salvati in cache, una delle novità offerte dall'infrastruttura di cache di ASP.NET 2.0.

Questa possibilità è disponibile anche su SQL Server 7 o 2000, ma la differenza risiede nell'architettura della versione 2005, che consente di sfruttare gli HttpListener di ASP.NET, così che sia SQL Server ad inviare le notifiche sui cambi nei risultati della query, anzichè essere ASP.NET a farne una ad intervalli regolari per verificare che i dati siano cambiati.

Tra l'altro questo consente di limitare di molto la fase di configurazione della dipendenza, che si limita ad abilitare il supporto del Service Broker sul database e dare l'accesso all'utente, il tutto eseguendo questi comandi:

ALTER DATABASE pubs SET ENABLE_BROKER
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO daniele

Per tenere in cache il risultato di una query fino a che la stessa non avrà modificato i propri dati è sufficiente associare il SqlCommand utilizzato ad una nuova istanza di tipo SqlCacheDependency, così che il risultato venga monitorato e l'oggetto in cache venga poi invalidato e rimosso in automatico.

C#

using (SqlConnection conn = new SqlConnection(Configuration.ConnectionString))
{
// query da eseguire
SqlCommand cmd = new SqlCommand("SELECT au_fname, au_lname FROM dbo.authors", conn);

SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = command;

// definizione della dipendenza
SqlCacheDependecy dep = new SqlCacheDependency(cmd);

// caricamento dei dati
DataTable authorsTable= new DataTable("Authors");
da.Fill(authorsTable);

// aggiunta in cache
Cache.Insert("Authors", authorsTable, dep);
}

VB

Dim conn as SqlConnection
Using (conn = New SqlConnection(Configuration.ConnectionString))
' query da eseguire
Dim cmd As SqlCommand =  New SqlCommand("SELECT au_fname, au_lname FROM dbo.authors",conn) 
 
Dim da As SqlDataAdapter =  New SqlDataAdapter() 
da.SelectCommand = command
 
' definizione della dipendenza
Dim dep As SqlCacheDependecy =  New SqlCacheDependency(cmd) 
 
' caricamento dei dati
Dim authorsTable As DataTable =  New DataTable("Authors") 
da.Fill(authorsTable)
 
' aggiunta in cache
Cache.Insert("Authors", authorsTable, dep)
End Using

Fino alla modifica della tabella Authors (nell'esempio si usa il database Pubs), i dati risultati dalla query rimarranno nella cache e pertanto l'elemento non verrà rimosso.

Ci sono alcune limitazioni nelle query che possono sfruttare questa modalità, come l'impossibilità di utilizzare * o la necessità di impostare anche l'owner della tabella, che sono tutte documentate qui.

 

Forzare il refresh della pagina nel browser

Di Daniele Bochicchio - Microsoft MVP

Spesso, quando si realizzano pagine web, si ha la necessità di applicare delle politiche di scadenza temporale alle stesse, così che il browser al successivo accesso non prelevi la versione che ha salvato nei file temporanei, come avviene di default, ma la richieda nuovamente al server.

Per far ricaricare la pagina ogni volta è necessario impostarne la scadenza, attraverso una semplice istruzione che prevede la valorizzazione della proprietà ExpiresAbolsute con un valore di tipo DateTime che in genere si imposta ad una data precedente a quella corrente.

C#
public partial class TestPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// notifichiamo i proxy che non devono tenere in cache
Response.CacheControl = "private";

// scadenza per il browser
Response.ExpiresAbsolute = DateTime.Now.AddDays(-365);
Response.Cache.SetCacheability(HttpCacheability.NoCache);

Response.Cache.SetLastModified(DateTime.Now.AddHours(-1));
}
}
VB
Public partial Class TestPage
 Inherits System.Web.UI.Page
Protected  Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
' notifichiamo i proxy che non devono tenere in cache
Response.CacheControl = "private"
 
' scadenza per il browser
Response.ExpiresAbsolute = DateTime.Now.AddDays(-365)
Response.Cache.SetCacheability(HttpCacheability.NoCache)
 
Response.Cache.SetLastModified(DateTime.Now.AddHours(-1))
End Sub
End Class

La proprietà CacheControl agisce invece sui proxy, che con il valore "private" non tengono più in locale una copia della pagina, rendendo la visualizzazione da parte dell'utente non vincolata ad una copia magari derivante da un visita di uno precedente.

Infine, SetCacheability indica ad alcuni browser, a cui non basta l'impostazione della scadenza temporale, che la pagina non deve essere salvata.

Un discorso a parte lo merita invece SetLastModified, che consente al browser di confrontare, mediante una richiesta con metodo HEAD e non GET, la copia locale con quella remota. Questa tecnica è da utilizzare quando si servono contenuti in formato RSS perchè consente di fare in modo che l'aggregator non scarichi ogni volta l'intero feed, ma confronti prima di tutto la data di aggiornamento del file.

 

Salvare il contenuto di una tabella di un database in formato XML con ASP.NET 2.0

Di Daniele Bochicchio - Microsoft MVP

XML rappresenta il formato più semplice con cui scambiare informazioni tra applicazioni di tipo differente.

Benchè ormai SOAP ed i web service siano considerati l'approccio migliore in questi casi, resta possibile creare una semplice pagina che sfrutti il meccanismo REST per restituire, attraverso una chiamata ad una pagina ASP.NET, il risultato dell'elaborazione di una query in formato XML.

In questo scenario, qualora i dati da mostrare non siano in quantità eccessiva, può ritornare utile la classe DataTable, che nella versione 2.0 ha le stesse caratteristiche DataSet offre sin dalla prima release e consente di scrivere in uno stream il proprio contenuto in formato XML.

La proprietà OutputStream della classe Response ci consente di agganciarci allo stream e scriverci direttamente, per cui per avere l'effetto desiderato è sufficiente implementare il codice che segue.

C#
// carico i dati usando il SqlDataAdapter
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM authors", ConfigurationManager.ConnectionStrings
["SqlServer"].ConnectionString);
DataTable table = new DataTable("Authors");
da.Fill(table);

// scrivo a video il contenuto in format XML
Response.ContentType = "text/xml";
table.WriteXml(Response.OutputStream);

VB
' carico i dati usando il SqlDataAdapter
Dim da as SqlDataAdapter = new SqlDataAdapter("SELECT * FROM authors", ConfigurationManager.ConnectionStrings
("SqlServer").ConnectionString)
Dim table as DataTable = new DataTable("Authors")
da.Fill(table)

' scrivo a video il contenuto in format XML
Response.ContentType = "text/xml"
table.WriteXml(Response.OutputStream)