Paolo Pialorsi

SOA, Workflow Foundation (WF), Windows Communication Foundation (WCF) e le Architetture Distribuite

News

Archives

May 2005 - Posts

DevCon OneDay Security: ASP.NET e SOA

I prossimi 20 e 21 luglio io e RoB terremo a Milano (presso il centro conferenze di Microsoft Italia) due giornate DevCon OneDay sul tema della sicurezza. Ecco la URL al sito: http://devcon.devleap.com/OneDay/.

La prima giornata sarà dedicata alla sicurezza di ASP.NET 1.x e 2.0 in tutte le sue forme, come è già stato fatto a Firenze due mesi fa, mentre la seconda giornata si occuperà della sicurezza nello sviluppo di applicazioni in architettura SOA, non soffermandosi solo sulla parte SOAP, ma analizzando anche aspetti legati alla security dello strato business e DAL di un'architettura distribuita.

Qui trovate le due agende:

http://devcon.devleap.com/PublicSite/DevConOneDayASPNETSecurityDettaglioAgenda.psx
http://devcon.devleap.com/PublicSite/DevConOneDaySOASecurityDettaglioAgenda.psx

che ovviamente ci piaccino molto :-) ... se volete iscrivervi potete seguire questo link. Se volete invece maggiori informazioni non esitate a contattarci.

Posted: May 28 2005, 12:58 PM by paolo
Filed under: ,
Indigo: TCP, UDP e XmlFormatter

Spronato :-) dalla richiesta di un amico, questa sera prima di andare a dormire ho preparato un esempio di servizio Indigo (Beta 1 RC, l'ultimo insomma) che espone una semplice operazione (indovinate un po'?! .... La prenotazione di una camera in un hotel ... l'avete mai vista? :-) ...). Il servizio serializza con XmlFormatter (il nuovo serializzatore di .NET 2.0 e di Indigo) un oggetto definito nel modo seguente:

 [DataContract(Namespace="http://schemas.devleap.com/HotelBookingInfo")]
 public class HotelBookingInfo
 {
  private Int32 _hotelId;
  private String _hotelName;
  private String _customerFullname;
  private DateTime _checkInDate;
  private DateTime _checkOutDate;
  private Decimal _euroPerNight;

  [DataMember("idHotel")]
  internal Int32 HotelId
  {
   get { return (this._hotelId); }
   set { this._hotelId = value; }
  }

  [DataMember("Hotel")]
  public String HotelName
  {
   get { return (this._hotelName); }
   set { this._hotelName = value; }
  }

  [DataMember("Customer")]
  public String CustomerFullname
  {
   get { return (this._customerFullname); }
   set { this._customerFullname = value; }
  }

  [DataMember("in")]
  public DateTime CheckInDate
  {
   get { return (this._checkInDate); }
   set { this._checkInDate = value; }
  }

  [DataMember("out")]
  public DateTime CheckOutDate
  {
   get { return (this._checkOutDate); }
   set { this._checkOutDate = value; }
  }

  [DataMember("euroPrice")]
  public Decimal EuroPerNight
  {
   get { return (this._euroPerNight); }
   set { this._euroPerNight = value; }
  }
 }

Si noti la visibilità della proprietà HotelId, che è internal ma sarà comunque esposta da XmlFormatter, che prescinde dal contratto interno al codice e definisce un contratto alternativo, assolutamente orientato al messaggio e astratto dalla infrastruttura sottostante.
Il servizio è poi esposto (da codice, ma poteva esserlo anche da file .config) sia tramite TCP (moniker: net.tcp) che UDP (moniker: soap.udp):

Ecco il servizio e il suo contratto (come interfaccia .NET):

 [ServiceContract(FormatMode = ContractFormatMode.XmlFormatter,
  Namespace="
http://schemas.devleap.com/HotelBooking",
  Style=ServiceOperationStyle.DocumentBare,
  Use=ServiceOperationBindingUse.Literal)]
 public interface IHotelBooking
 {
  [OperationContract(Action="urn:BookHotel")]
  Guid BookHotel(HotelBookingInfo hotelBookingInfo);
 }

 public class HotelBooking: IHotelBooking
 {
  #region IHotelBooking Members

  public Guid BookHotel(HotelBookingInfo hotelBookingInfo)
  {
   return (Guid.NewGuid());
  }

  #endregion
 }

Ed ecco il codice dell'host del servizio:

  static void Main(string[] args)
  {
   Binding udpBinding = new SampleProfileUdpBinding(true);
   Uri udpUri = new Uri("soap.udp://localhost:35001/HotelBooking/");

   ServiceHost<HotelBookingService.HotelBooking> svc = new ServiceHost<HotelBookingService.HotelBooking>();
   svc.AddEndpoint(typeof(HotelBookingService.IHotelBooking), udpBinding, udpUri);

   using (svc)
   {
    svc.Open();
    Console.WriteLine("Host in ascolto");
    Console.ReadLine();
   }
  }

Infine il client che lo utilizza e che si appoggia a codice del proxy generato in automatico con l'utility SvcUtil.exe:

  static void Main(string[] args)
  {
   schemas.devleap.com.HotelBookingInfo.HotelBookingInfo hotelInfo =
    new schemas.devleap.com.HotelBookingInfo.HotelBookingInfo();
   hotelInfo.idHotel = 10;
   hotelInfo.Hotel = "Hotel di Casa";
   hotelInfo.Customer = "Paolo Pialorsi";
   
hotelInfo.@in = DateTime.Now.AddDays(1);
   
hotelInfo.@out = DateTime.Now.AddDays(2);
   hotelInfo.euroPrice = 120;

   HotelBookingProxy svcProxy = new HotelBookingProxy(
    new EndpointAddress(new Uri("soap.udp://localhost:35001/HotelBooking/")),
    new SampleProfileUdpBinding(true));

   IAsyncResult ar = svcProxy.BeginBookHotel(hotelInfo, null, null);

   Boolean beforeTimeout = ar.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(5000), false);
   if (beforeTimeout)
   {
    Guid reservationId = svcProxy.EndBookHotel(ar);
    Console.WriteLine("Conferma prenotazione: {0}", reservationId);
   }
   else
    Console.WriteLine("Timeout!");

   Console.ReadLine();
  }

La richiesta che mi ha dato la spinta a preparare questo mini-semplice-esempio è il fatto che vi era l'esigenza di esporre il servizio via UDP, ma a prima vista il trasporto UDP risultava rimosso da Indigo. In effetti ora il trasporto UDP non è più nativo, ma è per ora presente come esempio di trasporto custom nel SDK, quindi possiamo dire che c'è comunque. Io mi sono limitato a compilare il sample (presente in Samples\Indigo\Extensibility\UdpTransport) e a referenziare l'assembly nel mio progetto. Il resto è tutto già fatto. Inoltre ho preparato comunque anche un esempio che usa TCP (net.tcp).

Qualora l'esempio vi interessi, potete scaricare da qui uno ZIP con dentro il tutto codice, sia della soluzione UDP che TCP.

Posted: May 26 2005, 12:57 AM by paolo
Filed under:
WinFX: Indigo e Avalon Beta 1 RC e Change-Log

Come ormai tutti sappiamo sono disponibili delle nuove build di Indigo e Avalon ai seguenti indirizzi (come ISO e non solo come BootStrapper):

WinFX Beta 1 RC
http://download.microsoft.com/download/2/b/0/2b02bca5-1df1-46fd-ae8a-b87dc4d2bf2f/en_avalon_indigo_redist_beta1_RC.iso

WinFX Beta 1 RC SDK (con esempi e documentazione, quindi)
http://download.microsoft.com/download/5/4/0/5407D6E4-740E-413E-8E91-EF7AC53A478A/en_winfx_sdk_beta1_RC.iso

Qui c'è un utile change-log rispetto alle build precedenti, grazie al contributo di Drew Marsh.

Posted: May 25 2005, 10:27 PM by paolo
Filed under: ,
OPENXML e Sharepoint

Questa sera ho risolto un problema di prestazioni che mi assillava da parecchi giorni. Avevo l'esigenza di restituire una lista di item di Sharepoint, filtrati in base a dei record che avevo in un database applicativo. La questione, di per sé banale, diventava critica dato che i record sia nel DB che in Sharepoint eranno diverse migliaia e non potendo (o meglio dire non volendo ... per non andare fuori dal modello ad oggetti del SDK di WSS/SPS) fare le query con una JOIN sul DB di Sharepoint, dovevo fare un pezzo di query con T-SQL e un altro con CAML ripetendo tra l'altro N volte la query CAML per esigenze mie applicative.

Ovviamente il risultato, dal punto di vista delle prestazioni, era molto deludente, tanto che, trattandosi di dati non troppo variabili nel tempo, avevo deciso di fare caching per 60 minuti del risultato, per rendere un po' meno pesante agli utenti la cosa (sono quelle "pezze" che non ti rendono mai troppo sereno se ami fare bene le cose....).

Poi questa sera - preso da un colpo di fulmine - ho trovato la soluzione ai miei problemi! Ho pensato: da una lista Sharepoint posso ottenere l'elenco dei record in formato XML. In SQL Server 2000 posso eseguire delle query OPENXML su un blob di testo che rappresenti un documento XML well-formed. Facendo 1+1=2 ho trovato la soluzione! Ora ho una stored procedure che fa la JOIN tra i dati applicativi in SQL e i dati ottenuti in XML da Sharepoint come se fossero una tabella di SQL (in effetti sotto OPENXML il buon SQL Server mi fa una tabella temporanea) e il gioco è fatto. Ora siamo passati da oltre 1 minuti di tempo di elaborazione (per me inaccettabile!!! Per questo non mi davo pace!) a pochi secondi di cui la gran parte sono spesi per eseguire una trasformazione XSLT del risultato e per passarlo via HTTP al client.

Riassumendo i punti chiave dell'attività:

  • Ottengo tutti gli item della lista Sharepoint con: myList.Items.Xml
  • Li carico come parametro di una stored procedure con una definizione simile a questa:

CREATE PROCEDURE dbo.spListXmlItems (
@XmlContacts ntext
)
AS
DECLARE @idXmlContacts int

exec sp_xml_preparedocument @idXmlContacts OUTPUT, @XmlContacts, '<root xmlns:z="#RowsetSchema" xmlns:rs="urn:schemas-microsoft-com:rowset"/>'

-- Qui in realtà nella mia SP faccio la JOIN con la tabella applicativa
SELECT * FROM OPENXML(@idXmlContacts, '/xml/rs:data/z:row', 1)

exec sp_xml_removedocument @idXmlContacts

  • Si noti il fatto che ho dovuto dichiarare i namespace XML con prefisso rs e z, che rappresentano i dati in formato ADODB.Recordset, usato anche da Sharepoint per rappresentare gli item di una lista
  • Nel mio caso poi il risultato è ancora un XML (infatto la stored procedure esegue una SELECT ... FOR XML EXPLICIT della JOIN tra la OPENXML e la tabella applicativa del mio DB SQL) per dare un risultato che deve essere elaborato in una trasformazione XSLT e restituito al client via HTTP
  • Spero che la mia folgorazione :-) di stasera possa magari essere utile anche ad altri, quindi ne ho lasciata traccia anche qui.

    Posted: May 23 2005, 12:40 AM by paolo
    Filed under:
    I'm back! :-)

    Sono rientrato ieri sera da 15gg abbastanza pieni e all'estero. Da oggi dovrei riuscire ad essere di nuovo tra i vivi.
    Ho parecchie cose da dire e scrivere, quindi nei prossimi giorni cercherò di sfogare un po' i miei istinti repressi :-) di blogger.

    Nel frattempo vi pongo una domanda alla quale potete rispondere nei commenti o via email (paolo@devleap.com).
    Quanti di voi utilizzano BizTalk Server 2004? Se lo utilizzate che applicazioni/scenari di integrazione sviluppate in genere?

    PS: Notate che ho aggiunto una categoria ai miei blog ;-) ... vorrei capire se interessa a qualcuno e semmai in quali ambiti. Grazie.

    Microsoft Office InfoPath 2003 Toolkit for Visual Studio 2005 Beta 2

    Bene! Appena rientro lo provo:

    http://blogs.msdn.com/vsto2/archive/2005/05/05/415003.aspx

    Posted: May 12 2005, 09:40 AM by paolo
    Filed under:
    Deployment di soluzioni Sharepoint "impegnative"

    Forse qualcuno di voi si starà chiedendo se sono ancora vivo :-) ... beh sì diciamo di sì :-) !

    Sono in un periodo di full immersion su progetti (2 in particolare) che stanno per fortuna volgendo al termine, ma che non mi lasciano molto tempo per tutto il resto (blog, articoli, eventuali libri, ecc.). Sorry.

    In particolare ho pressochè concluso in questi ultimi giorni il deployment di una soluzione Sharepoint (Windows Sharepoint Services per l'esattezza) mediamente impegnativa :-) e voglio rendervi partecipi di alcune considerazione che possono sempre venire utili.

    La soluzione riguardava una gestione di contatti e documenti collegati ai contatti. Si parla di circa 7.000 contatti con circa 14.000 document libraries ad oggi riempite con 130.000 documenti di vario genere (PDF, MSG, EML, GIF, TIFF, DOC, XLS, ecc.). Il database dei contenuti cuba 16GB, quindi non piccolo ma nemmeno enorme (per quello che può fare SQL Server, quantomeno).

    Aspetti e elementi da tenere presenti, qualora doveste creare e alimentare in modo automatizzato strutture dati Sharepoint di dimensioni simili:

    • Su disco i documenti occupavano circa 7GB, spostati nel DB siamo arrivati a 16GB di file di database
    • Durante l'upload ho messo il database in modalità di backup "Simple" onde evitare di avere log delle transazioni enormi
    • Caricare i documenti, non con un semplice upload di massa, ma facendo per ogni documento una serie di valutazioni sul suo contenuto e su degli item già presenti in WSS, per compilare delle metainformazioni e incasellare i documenti nella document library corretta, via Web Service, ha richiesto quasi 4gg di lavoro di un'applicativo di migrazione scritto ad hoc (ha girato da sabato pomeriggio a questa mattina alle 5!)
    • In media un documento di office classico (60Kb) ci ha messo 2 secondi a entrare nello storage di WSS, ripeto anche a causa delle valutazioni da fare (per lo più con query CAML) sui dati già presenti in WSS. Messo sotto stress, cioè 1 documento ogni 2 secondi per 4gg :-), WSS ha creato qualche problema al processo di ASP.NET che all'incirca ogni 10 ore aveva l'esigenza di essere riavviato, principalmente per eccessivo utilizzo di memoria.
    • Le prestazioni di WSS, al crescere del volume di dati, sono rimaste costanti, non troppo brillanti a dirla tutta, ma costanti. Questo significa che Sharepoint è effettivamente un prodotto scalabile (cioè che al crescere dei volumi di lavoro/traffico, mantiene costanti le proprie prestazioni, senza per questo poter assumere che le prestazioni siano brillanti, solo costanti appunto)
    • Il linguaggio CAML applicato iterativamente su N query diverse, ma sulla stessa lista, non è troppo di soddisfazione (forse anche perchè siamo abituati alle query SQL "classiche"), nel senso che lavorando su quantità di dati significative le prestazioni complessive non sono troppo soddisfacenti. In particolare avevo l'esigenza di creare una JOIN tra dati presenti in un DB applicativo (SQL Server) e delle liste in WSS. Non volendo andare "a maiale" :-) sul DB di WSS, ma volendo usare il suo modello ad oggetti - per rimanere nello standard - ho utilizzato delle query CAML per le estrazioni dei gruppi di record.
    • Per la parte di codice in produzione (ricerche custom, smanettamenti vari sugli item in base ad altri item di altre liste, ecc.) ho utilizzato sia query CAML che altre strade. Le query CAML infatti non davano sempre risultati egregi in termini di prestazioni, soprattutto quando sono da eseguire N volte con N diverse condizioni con X records dove X è nell'ordine di qualche migliaio, quindi ho poi optato per del caching in memoria dei dati non variabili (sotto forma di XML, usando l'XML restituito dal modello ad oggetti di Sharepoint, quindi le :-S !) e per delle ricerche dirette sullo snapshot in memoria (o con XPath o importando in un DataSet i dati). In alcuni casi ho sfruttato delle trasformazioni XSLT per fare il merge tra i dati in WSS e i dati in SQL Server (nel DB applicativo), visto che poi il risultato doveva essere del contenuto XML da restituire tramite Web Service.
    • Sicuramente è più facile realizzare una soluzione WSS in cui si parte con lo storage vuoto e lo si riempie strada facendo, ma questo è scontato. L'idea che mi sono fatto comunque è che il motore di WSS non sia stato pensato troppo nell'ottica di possibili/eventuali operazioni di bulk load.
    • Rimango assolutamente dell'idea che XML sia uno strumento fantastico :-), se applicato a piccole porzioni di dati o brevi messaggi (ad es. SOAP), come ho sempre manifestato sia nei corsi e conferenze che nei miei articoli e libri, ma che diventa uno strumento lento nel caso di moli significative di dati (qualche MB sono già dimensioni significative!).

    Per le rifiniture dell'applicazione aspetto di rientrare da questi 10gg di trasferta (ora sono a Monaco con Marco per la SQL Pass 2005) e la prossima settimana sarò a Nizza a seguire il Deep Training su BizTalk 2004 offerto da Microsoft (ce l'ho fatta a passare la selezione :-) !). Nel frattempo dovrò necessariamente chiudere l'altro progetto aperto, del quale mi sono portato qui a Monaco un promemoria vivente, giusto per non dimenticarmi :-)! Se durante l'uso della soluzione WSS emergeranno altre situazioni/problematiche degne di nota non mancherò di lasciarne traccia qui, così che possano venire utili ad altri.

    Posted: May 12 2005, 12:23 AM by paolo | with 1 comment(s)
    Filed under: ,