Interfaccia di amministrazione per Rhino Security


Per chi usa o è intenzionato ad usare Rhino Security, ne abbiamo implementato una "console" di amministrazione.

Al momento supporta la gestione di utenti, gruppi, operazioni e permessi, quindi solo una parte di quello che fornisce RS. Comunque potrebbe essere un buon starting point, anche perchè è open source.

author: Stefano Martinz | posted @ martedì 20 luglio 2010 17:19 | Feedback (16)

[OT] Sviluppatori senior cercasi


Ciao a tutti,

NEXiDA, ditta dove lavoro, sta cercando sviluppatori senior (.net ma non solo).
Possibilita' di lavorare su prodotti (code generation) e progetti.
 
Per maggiori dettagli date un occhio al sito dove sono presenti anche i contatti - http://www.nexida.com

stefano

author: Stefano Martinz | posted @ martedì 9 marzo 2010 02:57 | Feedback (1)

Comunicare le intenzioni di un metodo attraverso la sua signature


Ieri stavo debuggando un componente per risolvere una serie di bugs, quando, dopo un buon numero di F11 arrivo a un metodo che prende in input una collection e restituisce una collection dello stesso tipo. La signature è più o meno come questa:

private static List<int> ApplyCustomFilter1(List<int> ints)

E questo assomiglia al codice che lo usa:

List<int> ints = new List<int> { 0, 1, 2, 3 };
var filteredInts = ApplyCustomFilter1(ints);
// altro codice che lavora su ints e filteredInts

Ok, quelle tre righe mi hanno fatto perdere mezz'ora :(

Il problema è che non posso assumere nulla sulla lista ‘ints’ dopo che il metodo ‘ApplyCustomFilter1’ è stato eseguito. Quale di questi assert sarà soddisfatto?  

Debug.Assert(object.ReferenceEquals(ints, filteredInts));
Debug.Assert(!object.ReferenceEquals(ints, filteredInts));
Debug.Assert(ints.Count == 4);
Debug.Assert(ints[0] == 0 && ints[1] == 1 && ints[2] == 2 && ints[3] == 3);

Non lo so! E non lo posso sapere senza andare a vedere la sua implementazione interna, o, per essere più precisi: guardando l’implementazione, posso capire cosa succede in determinate circostanze, ma non sono sicuro di poter garantire il determinismo degli asserts.

E se il metodo fosse stato un po’ più esplicito:

private static void ApplyCustomFilter2(List<int> ints)

Mmm, in questo modo non mi devo porre il problema di una seconda lista. Sono quasi sicuro che il metodo modificherà l’input. Per essere sicuro al 100% avrei potuto usare ref:

private static void ApplyCustomFilter3(ref List<int> ints)

Ok, “ApplyCustomFilter3” è più esplicito sulle sue intenzioni e su come le esegue, mi garantisce che le modifiche verranno fatte sulla mia lista in input. Potrei accontentarmi, ma oltre a non essere un gran fan di ref/out, preferirei che il filtering sulla lista venisse applicato senza side effects, cioè senza modificare nessuno degli input parameters. 

Se provo a seguire una delle regole della programmazione funzionale, e cioè che una funzione non può avere side effects e deve sempre ritornare un risultato, allora:

private static List<int> ApplyCustomFilter4(IEnumerable<int> ints)

A questo punto posso assumere qualcosa:

ints = new List<int> { 0, 1, 2, 3 };
newFilteredInts = ApplyCustomFilter4(ints);
Debug.Assert(!object.ReferenceEquals(ints, newFilteredInts));
Debug.Assert(ints.Count == 4);
Debug.Assert(ints[0] == 0 && ints[1] == 1 && ints[2] == 2 && ints[3] == 3);

’ApplyCustomFilter4’ è esplicita sulle intenzioni e mi garantisce che l’input non verrà modificato.

In realtà la garanzia sul fatto che l’input non venga modificato è vera in questo particolare esempio dove l’input è IEnumerable<T> con T che è un value type (quindi immutabile). Se T fosse un reference type, quindi mutable, avrei solo la garanzia che la sequenza non possa essere modificata, ma non potrei assumere nulla sul valore degli oggetti T contenuti (vedi immutable facades). In altre parole l’ultimo Assert non è garantito se T è un reference type. 

author: Stefano Martinz | posted @ lunedì 3 agosto 2009 21:22 | Feedback (0)

Mocking WindowsIdentity with Moq


Premessa: non c’è nulla di nuovo in questo post. Le tecniche mostrate sono comuni e conosciute quando serve il mock di una classe di terze parti che non è completamente mockabile. Il post potrebbe però salvare 30 minuti a qualcuno che ha bisogno del mock di WindowsIdentity. Da usarsi a proprio rischio e pericolo...

author: Stefano Martinz | posted @ giovedì 23 luglio 2009 22:46 | Feedback (3)

A (s)lap around(to) Windows Azure


Azure, Azure…tutti ne parlano. Mi è venuta voglia di provarlo

Non mi metterò qui a rispiegare cos'è e come funziona. Tanto è già stato detto ed esistono diversi tutorials e video che ne spiegano i componenti. 

Una cosa però vorrei riportare dal sito ufficiale a proposito dei servizi forniti per lo storage dei dati:

Simple data storage services

  • Blobs, tables, and queues hosted in the cloud, close to your computation
  • Authenticated access and triple replication to help keep your data safe
  • Easy access to data with simple REST interfaces, available remotely and from the data center

Paura

REST??? Per accedere ai dati???

Tutte le volte che devo accedere al datastore devo usare http? E ricevere i dati in formato Atom, o simili? Mi stanno dicendo che non posso accedere ai dati con altri protocolli a più basso livello? E che se ho un’applicazione che usa ADO.NET non posso installarla su Windows Azure?

Un sospiro di sollievo

Imparo oggi (o mi sembra di capire) che SQL Data Services adesso si chiama SQL Azure, che supporterà ADO.NET e TDS e che sarà utilizzabile direttamente da Windows Azure. L’annuncio che SDS avrebbe supportato puri db relazionali (grazie a customer feedback) era di metà maggio, ma non avevo ancora trovato nessuna conferma che sarebbe stata possibile l’integrazione con Windows Azure. Questa è una buona notizia.

Non è ancora disponibile da provare però.

Test

Siccome non posso provare la parte con db relazionali, provo almeno TableStorage.

Scarico la CTP di Maggio e uno degli esempi che ci sono in rete per sporcarmi le mani e analizzare un po’ di numeri. L'esempio e' completo e mostra più e meno tutto: un Web Role con una ASP.NET web app + qualche servizio WCF, dati distribuiti su TableStorage e BlobStorage e anche l'utilizzo di Queues attraverso 2 istanze di Worker Roles. Quello che a me interessa però è molto semplice: capire la latency di quelle chiamate REST dal Web Role al Table Storage. In soldoni, quanto tempo ci vuole per recuperare dati dall’application layer.

Quindi, aggiungo una pagina al progetto, e faccio il deployment in the cloud - http://smartinzhs.cloudapp.net/LatencyTest.aspx (da qualche giorno il sito va a intermittenza…).

Il test interessante è “GetAll”UserItemsTable.

Il codice è semplicissimo e si noti che calcolo solo il tempo effettivo per fare la chiamata. Quindi solo l’hop dal Web Role al TableStorage e ritorno – non tutto il round trip dal browser.   

protected void btnGetAll_Click(object sender, EventArgs e)
{
var itemService = new ShortItemService();
Stopwatch sw = new Stopwatch();

sw.Start();
var items = itemService.RetrieveAllItems();
sw.Stop();
...
}

RetrieveAllItems() non fa altro che fare “select * from UserItems”.

Con i dati presenti, la dimensione media di un record è di 900Bytes (senza differenze estreme). Il numero di record totali nella tabella può oscillare nel tempo per come è stata disegnata l’applicazione che simula un sito di aste online. Quando eseguita in locale la query produce questa chiamata REST: “http://127.0.0.1:10002/devstoreaccount1/UserItems()” e ritorna una serie di entry come la seguente in format Atom:

<entryxml:base=http://127.0.0.1:10002/devstoreaccount1/ xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:etag="W/&quot;datetime'2009-06-23T19%3A36%3A52.517'&quot;" xmlns="http://www.w3.org/2005/Atom">
  <
id>http://127.0.0.1:10002/devstoreaccount1/UserItems(PartitionKey='f1733617-df03-69b6-fae8-d1971a647c30',RowKey='1633813563221000000_8c216797-6e19-4770-9314-4704f3daef75')</id>
  <
title type="text"></title>
  <
updated>2009-07-07T01:44:13Z</updated>
  <
author>
    <
name />
  </
author>
  <
linkrel="edit" title="UserItems" href="UserItems(PartitionKey='f1733617-df03-69b6-fae8-d1971a647c30',RowKey='1633813563221000000_8c216797-6e19-4770-9314-4704f3daef75')" />
  <
category term="BidNow.UserItems" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
  <
content type="application/xml">
    <
m:properties>
      <
d:ItemId>8c216797-6e19-4770-9314-4704f3daef75</d:ItemId>
      <
d:Title>The Pragmatic Programmer</d:Title>
      <
d:ShortDescription>From Journeyman to Master</d:ShortDescription>
      <
d:EndDate m:type="Edm.DateTime">2009-06-23T12:18:42.1</d:EndDate>
      <
d:ThumbnailUrl>http://127.0.0.1:10000/devstoreaccount1/photos/thumbnails/8c216797-6e19-4770-9314-4704f3daef75.jpg?timeout=30</d:ThumbnailUrl>
      <
d:PhotoUrl>http://127.0.0.1:10000/devstoreaccount1/photos/8c216797-6e19-4770-9314-4704f3daef75.jpg?timeout=30</d:PhotoUrl>
      <
d:Timestamp m:type="Edm.DateTime">2009-06-23T19:36:52.517</d:Timestamp>
      <
d:PartitionKey>f1733617-df03-69b6-fae8-d1971a647c30</d:PartitionKey>
      <
d:RowKey>1633813563221000000_8c216797-6e19-4770-9314-4704f3daef75</d:RowKey>
    </
m:properties>
  </
content>
</
entry>

Risultati

I numeri parlano da soli: 1.8secs per circa 80 records, con picchi fino a 2.3secs! Significa 2 secondi per estrarre 72KB dal data store!!! E la ciliegina: un record su db si aggira sui 900Bytes, on the wire diventa pi'u’ di 2KB a causa della format atom.

Inutile provare la stessa cosa con ADO.NET che usa TDS, sappiamo tutti che è mooolto più veloce e molto piu’ compatto; anche via TCP con client and db su macchine diverse.

Considerazioni

Di solito, si ha la percezione che un sito e’ veloce quando le richieste (o parte di esse) ci vengono servite sotto i 2 secondi in media (e sono stato largo). Secondo i dati di cui sopra, usando REST, Azure si mangia tutto il mio margine solo per accedere al db!

Ovvio il perché di REST per scenari dove è necessario accedere a dati “in the cloud” da ovunque, quindi l’unica opzione è HTTP. Posso vederne un utilizzo in termini di interoperabilita’. Ma nel caso di Windows Azure, dove application tier e dati sono entrambi in the cloud?

Per ora i dati ritornati dal TableStorage sono in semplice formato xml. Da quello che vedo con fiddler sui test in locale, il messaggio non è compresso e nulla viene fatto per minimizzarlo. Non ho idea se in the cloud il comportamento sia diverso…ma dubito, visto i tempi…

Se optassi per usare Table Storage con REST, dovrei progettare l’architettura diversamente da come la progetterei se avessi ADO.NET e TDS. Qui il problema non sta nel creare un livello di astrazione che mi renda trasparente il fatto che uso TableStorage invece di SqlServer (che non sarebbe neanche così difficile) ma piuttosto il fatto che il livello di chattiness accettabile per SqlServer, potrebbe non esserlo per TableStorage e viceversa, visto che i protocolli usati sono così differenti (tralasciamo l’integrazione di ORM).

Non riesco a pensare a tipiche applicazioni web (penso a SaaS per esempio), dove latency e responsivness sono requisiti critici, implementate utilizzando TableStorage e REST come stanno al momento. Tutt’altro discorso se si tratta di long running workflows.

Non c’e’ dubbio che la scalabilità sia garantita e se a ciò si aggiunge che tutti i problemi operativi in termini di infrastruttura diventano di Microsoft, la piattaforma è veramente interessante a patto che vadano bene i costi. Una volta che ci si orienta nel mare di nuovi termini l’esperienza per un developer non è neanche così male…una volta sviluppata l’applicazione questo è tutto ciò che si può fare in the cloud :)

CropperCapture[4]

Devo ricordare che Windows Azure (come anche SQLDataServices/SQLAzure) è ancora in CTP, quindi le cose cambieranno. Come? Chi lo sa… Cambieranno anche in termini di latency? I numeri adesso sono veramente poco incoraggianti. Spero nel supporto per accesso ai dati classico.

 

Sarei curioso di sapere se qualcun altro ha avuto altre esperienze e risultati diversi.

author: Stefano Martinz | posted @ giovedì 16 luglio 2009 01:03 | Feedback (3)