Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Archive for the '.net' Category

Log4Net Custom Properties

Posted by Ricibald on 8th November 2013

Log4Net Log Custom Properties

From Tim Lewis’s answer on stackoverflow:

Since ASP.NET does not guarantee that the entire page request will be processed on the same thread, I prefer getting the answer from HttpContext.Current as log4net processes the logging event.

The following GetCurrentPage class implements what the log4net manual calls an “Active Property Value” by overriding its ToString method:

public class GetCurrentPage
{
  public override string ToString()
  {
      if (null != HttpContext.Current)
      {
          return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath;
      }
      return string.Empty; // or "[No Page]" if you prefer
  }
}

Register this class in Global.asax’s Application_Start in log4net’s GlobalContext.

protected void Application_Start(object sender, EventArgs e)
{
    XmlConfigurator.Configure();
    GlobalContext.Properties["page"] = new GetCurrentPage();
}

When log4net writes the %property{page} part of the line it will call the ToString method of our GetCurrentPage class which will lookup the value in the current request.

The same is possible to log the current HttpContext.

Posted in .net | No Comments »

A Strange ReportViewer Error

Posted by Ricibald on 11th October 2013

When I visualize my report using SSIS I have the following error:

One or more data sources is missing credentials

After hours of investigation I’ve realized a very strange thing:

If you iterate the ReportViewer’s Controls property in the Init phase (OnInit, Page_Init) you get the error “One or more data sources is missing credentials

To fix it just skip the iteration of Controls property :-)

Posted in .net | 2 Comments »

Where condition: IEnumerable vs IQueryable

Posted by Ricibald on 8th August 2013

Check this LINQ To Entities query

    IEnumerable<Book> books = ObjectContext.Books.Where(x => x.Id == 23);
    books = books.Where(x => !x.IsActive);

The query is, as expected, defferred: only when iterating we’ll get the value.

But…this is the query it produces (!!!):

SELECT *
FROM [dbo].[Books]
WHERE [Id] = 23

But… wait!! And the IsActive condition? This is the trap…!!

Solving the main trap

The extension method where exists in two forms:

If at compile time whe are using the first extension method we’ll execute query using LINQ To Objects. It’s deferred, but the filter is applied in memory on the entire result set and doesn’t filter in the data source…!!!

To fix it, “just” make sure to always use IQueryable.Where:

    IQueryable<Book> books = ObjectContext.Books.Where(x => x.Id == 23);
    books = books.Where(x => !x.IsActive);

Or… better (another reason to always use var!!):

    var books = ObjectContext.Books.Where(x => x.Id == 23);
    books = books.Where(x => !x.IsActive);

This produces the following correct query:

SELECT * 
FROM [dbo].[Books]
WHERE ([Id] = 23) AND ([IsActive] <> cast(1 as bit))

Solving the dependent trap

Now that we know the rule… take attention to write a similar code or you’ll have the same error:

void Test() 
{
    IQueryable<Book> books = ObjectContext.Books.Where(x => x.Id == 23);
    books = FilterActive(books);
    books.Dump();   // show data
}

IQueryable<Book> FilterActive(IEnumerable<Book> books) 
{
    return books.Where(x => !x.IsActive);
}

In the example you are filtering using IEnumerable.Where and this leads to the same error. You need to take in input IQueryable collection.

However, if you need to use IEnumerable because you’ll want to be indipendent of IQueryable you can write the following and it will work:

void Test() 
{
    var books = ObjectContext.Books.Where(x => x.Id == 23);
    books = FilterActive(books).AsQueryable();
}

IEnumerable<Book> FilterActive(IEnumerable<Book> books) 
{
    return books.AsQueryable().Where(x => !x.IsActive);
}

This is a trap, but not a bug. This strange behavior enable this scenario (thanks to @Enzo): the DAL works internally passing IQueryable objects and returns to the caller only IEnumerable. The Business Layer uses the detached IEnumerable to refine the query in memory. The Presentation Layer execute only if needed the mixed query (because deferred).

Posted in .net | 1 Comment »

Crash Reporting in .NET

Posted by Ricibald on 6th August 2013

In your .NET windows application you can’t catch all exceptions. There are critical exceptions that leaves your application inconsistent: in this case let your app crash.

But wait… and error reports? You have two alternatives:

  • use the native Microsoft solution: Windows Error Reporting (WER). It’s not easy to customize but it’s fully integrated in Win OS (i.e. when your application freezes). Here how to use WER.
  • use an off-the-shelf crash reporting library:

Note: if your application is an ASP.NET application, just use the excellent ELMAH

Posted in .net | No Comments »

Scrum Start: come cominciare??

Posted by Ricibald on 2nd August 2013

Dal sito Scrum Alliance:

Scrum è un framework Agile finalizzato a completare progetti complessi. Nato per i progetti di sviluppo software, si adatta bene anche per qualunque progetto complesso o innovativo. Le possibilità sono infinite. Il framework Scrum è solo apparentemente semplice.

Questi i punti principali:

  • Il product owner (PO) crea una lista del desiderato ordinato per priorità chiamato product backlog
  • Durante lo sprint planning il team sceglie uno spaccato del desiderato dalla cima della lista, lo sprint backlog, e decide come implementare i pezzi scelti
  • Il team deve completare il lavoro entro un lasso di tempo definito: lo sprint (tra 2 e 4 settimane) ma si riunisce ogni giorno per valutare i progressi (daily Scrum)
  • Nel frattempo, lo ScrumMaster (SM) mantiene il team focalizzato sugli obbiettivi
  • Al termine dello sprint il lavoro dovrebbe essere potenzialmente consegnabile: pronto per l’uso da parte del cliente, per una demo verso uno stakeholder, per la messa in commercio
  • Lo sprint termina con una sprint review e una retrospettiva
  • Con l’inizio dello sprint successivo, il team sceglie un nuovo set dalla cima del product backlog e il ciclo ricomincia

Lo Scrum è semplice, l’adozione prevede una serie di pratiche complesse da padroneggiare. Esistono moltissimi libri e presentazioni a riguardo.

Ma come si comincia??

Qualsiasi guida Scrum spiega il processo a regime, ma non dettaglia gli step iniziali necessari per portare a regime questo processo. L’obbiettivo di questo articolo è dettagliare dal punto di vista pratico “cosa fare” prendendo spunto da questo libro:

  1. Il PO seleziona il team che lavorerà nel progetto
  2. il PO apre il portale TFS e crea un nuovo Team Project scegliendo come template “Scrum” e aggiungendo i relativi utenti del team
  3. il PO convoca il team e gli stakeholder principali per un requirement workshop iniziale.
    • Lo SM (o il PO se non presente) cerca di sensibilizzare i partecipanti sull’esigenza di non pretendere di definire da subito tutti i requisiti: si dovrebbe discutere solo di requisiti di alto livello, senza scendere in dettagli né in questioni tecniche. Lo SM (o il PO) deve moderare questo aspetto.
    • Per chiarire ulteriormente, lo SM/PO spiega il concetto di user story come un requisito espresso dal punto di vista dell’utente “Come XXX voglio fare YYY per ottenere ZZZ” senza andare oltre questa semplice frase ed entrare in dettagli
    • La durata della riunione è stabilita in 3 ore, organizzata in una stanza con una grande lavagna bianca e pennarelli colorati. Ogni postazione avrà postit e penne.
    • Dopo un brainstorming si arriverà a chiarire i vari user story
  4. il PO produce nel proprio ufficio un foglio excel alimentando il backlog con le user story individuate
  5. il PO ordina rapidamente il backlog nell’excel basandosi inizialmente sull’importanza ignorando l’effort richiesto
  6. il PO trascrive nel portale i vari backlog dell’excel creando così nuovi Product Backlog Item in TFS. Lascia molti campi vuoti come Effort, Acceptance Criteria, Test Cases.
  7. due giorni dopo il PO convoca una riunione con il team ed eventuali esperti per stabilire il Definition of Done (DoD) in cui si concordano le qualità minime che il codice deve sempre avere per considerare concluso un certo task (testato, installato, copertura codice 80%, …)
  8. dopo aver definito il DoD, il PO ha le informazioni necessarie per stimare il lavoro. Convoca quindi una riunione di poker planning utilizzando carte fisiche con valori ?, 0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100, infinito. Per ogni user story viene quindi stabilito il peso in story point in termini di confronto tra user story fra loro
  9. terminata la riunione il PO aggiorna l’Effort in story point dei vari Product Backlog Item in TFS
  10. prima di convocare lo sprint planning il PO esegue un risk assessment per riordinare i product backlog secondo quello con più alto rischio (non è legato con l’incertezza assoluta dei requisiti). Il risk assessment può essere fatto in diversi modi: un modo semplice è calcolare il rischio min-max come prodotto tra gravità e probabilità min-max (dipende da eventuali fattori di mitigazione). In TFS semplicemente si possono trascinare le varie user story per riordinare
  11. per mantenere in stato coerente il backlog (operazione di grooming) il PO ha bisogno anche del contributo del team. Il PO stima un 10% di tempo necessario per mantenere il backlog
  12. il PO sceglie la durata per uno sprint tra 2–4 settimane. Una durata bassa (2 settimane) consente di avere feedback più brevi sullo stato di avanzamento del rilascio
  13. il PO per stimare le tempistiche ha bisogno di conoscere la velocità del team (quanti story point in uno sprint). Per farlo ha bisogno prima della capacità in ore effettivamente lavorabili per poi stimare la relativa velocità sulle user story da implementare:
    • per calcolare le ore effettivamente lavorabili il PO considera i giorni lavorativi, le ferie o feste programmate, il tempo necessario per incontri e varie riunioni di Sprint Planning, Sprint Review, Retrospective. Dal risultato sul lavorabile il PO deve inoltre dedurre il tempo di grooming e di drag: tempo perso per attività non previste. In genere in un team nuovo e non affiatato avrà un drag del 25%. Le ore effettive lavorabili a persona devono essere riportate nel tab Capacity di TFS.
    • per ottenere la velocità iniziale del team il PO convoca una riunione di Sprint Planning preliminare. Durante la riunione il team decompone la user story in cima al backlog in vari task stimando per ogni task le ore necessarie. Alle ore ottenute sottrae la capacità del team calcolata precedentemente: se avanza tempo allora il team procede ad analizzare la prossima user story e così via. Se la user story in esame è troppo generica (epic user story) questa viene eliminata (nelle ultime versioni si può ulteriormente aggregare) a favore di user story più di dettaglio che a loro volta sono decomposte in task. Se avanza poco tempo conviene non tirare troppo la corda cercando di aggiungere un altro user story, specialmente se il team è nuovo. TFS è in grado anche di calcolare la previsione (forecast) su quanti sprint sono necessari per completare il backlog basandosi su una velocità del team dichiarata.
  14. Il PO aggiunge in TFS user story e relativi task individuati con corrispondenti ore. Le user story che sono “epic” vengono eliminate
  15. Il PO inizia il release planning basandosi sul forecast di TFS e categorizzando le user story in temi per trovare un filone comune al rilascio e così dare funzionalità al cliente interamente testabili. Poiché nel primo sprint ci saranno diversi chores (messa a punto degli strumenti di lavoro) il primo sprint avrà meno funzionalità testabili. Alcuni temi sono rilasciabili in uno sprint, altri temi più corposi saranno rilasciabili in due o più sprint, perciò non tutti i rilasci saranno regolari.
  16. con le informazioni a disposizione il PO può stendere dei rapporti excel di massima con il time plan e il budget di progetto calcolando stipendi, hardware, software.
  17. il progetto può ufficialmente partire e andare a regime!

Posted in .net | No Comments »

Il Teorema CAP – il motivo del NOSQL?

Posted by Ricibald on 25th July 2013

Il teorema CAP, noto anche come teorema di Brewer, afferma che è impossibile per un sistema informatico distribuito fornire simultaneamente tutte e tre le seguenti garanzie (le 3 CAP):

  • Consistency (tutti i nodi vedono gli stessi dati nello stesso momento)
  • Availability (ogni richiesta riceverà una risposta)
  • Partition Tolerance (il sistema continua a funzionare nonostante arbitrarie perdite di messaggi)

Secondo il teorema, un sistema distribuito è in grado di soddisfare al massimo due di queste garanzie allo stesso tempo, ma non tutte e tre. Da semplice congettura nata nel 2000, la teoria è stata dimostrata formalmente nel 2002 assurgendolo a teorema vero e proprio.

Il concetto di per sé è abbastanza chiaro: se vogliamo avere dati geograficamente distribuiti siamo costretti a sacrificare o la Consistency o la Availability.

La globalizzazione informatica e l’esigenza di trattare Big Data spinge a scalare orizzontalmente i dati usando macchine consumer replicandole a livello geografico per ragioni di economicità, scalabilità, disponibilità, reattività.

Stanno quindi avanzando modelli di memorizzazione che non si basano sul classico modello ACID (Atomicity, Consistency, Isolation, Durability) degli RDBMS, OODBMS, Cluster, Caching bensì si basano sul nuovo modello BASE (Basically Available Soft-state services with Eventual-consistency) che rilassa il concetto di consistenza a una consistenza eventuale (applicabile solo nei dati non critici).

Dimostrazione pratica del teorema

Il principio di esclusione legato al teorema CAP dipende dal meccanismo di replica delle informazioni.

In reti locali ad alta velocità tra i vari nodi si può usare la replica pessimistica senza degrado di performance. In tale replica la scrittura sul nodo A non è considerata conclusa finquando tutte le repliche non sono concluse tramite una transazione distribuita two-phase commit. Poiché la scalabilità sarà possibile solo su rete locale allora si sacrifica la Partition Tolerance ma si garantisce Consistency e Availability.

Se viceversa i nodi sono distribuiti su reti non affidabili e a bassa velocità in tal caso si deve usare la replica ottimistica promettendo che prima o poi la replica sarà fatta mediante un processo asincrono. In questo caso il nodo B potrebbe quindi in un certo momento temporale non avere ancora ricevuto la replica aggiornata dal nodo A. Se una richiesta arriva a B in queste condizioni allora il nodo può scegliere di:

  • restituire il dato potenzialmente obsoleto (si sacrifica la Consistency)
  • rispondere con un errore (si sacrifica la Availability)

Nella pratica è un problema?

Il teorema CAP è in realtà valido solo quando i vincoli iniziali non possono essere rilassati: se sono costretto a garantire che rispondano sempre gli stessi nodi in qualsiasi momento allora è per forza valido. Se invece posso permettermi brevi downtime di partizioni allora posso ottenere tutte le 3 CAP. Infatti un bilanciatore potrebbe implementare stratagemmi come quello di isolare le partizioni che ancora non hanno il dato replicato. In questo modo sarebbe possibile ottenere un db cluster replicato geograficamente.

Attenzione però in tal caso a mantenere un rapporto tra i nodi replicati e quelli ancora non replicati, altrimenti la Consistency non è comunque garantita. Date infatti le seguenti variabili:

  • N: n° di nodi coinvolti dalla replica
  • W: n° di repliche che devono confermare la ricezione dell’update prima che l’update si possa considerare concluso
  • R: n° di repliche che verranno contemporaneamente contattate al momento della read per essere sicuri della correttezza del dato

Se vale:

  • W + R > N allora la C di CAP è garantita
  • W + R <= N allora la C di CAP non è sacrificata e avremo una eventual consistency

Quindi se ho un dato replicato in 3 nodi di cui almeno 2 repliche garantite e devo leggere da 2 repliche allora 4 > 3, avrò una strong consistency.

Il mio db è già replicato anche in Cina… funziona e non sapevo tutto questo!

Una cosa è il backup, un’altra è la Partition Tolerance. Una cosa è un db di pochi GB un’altra è un Big Data di TB di dati. Il modello BASE si applica soprattutto su dati geograficamente distribuiti che devono:

  • ottenere la minima latenza geografica possibile
  • avere un disaster recovery senza interruzioni di servizio

Potremmo pensare che il problema non ci riguarda e sono temi ad appannaggio solo di siti miliardari come facebook, ma in realtà ci riguarda da vicino. Con l’avvento del cloud i costi variano di molto se si utilizza o meno un datastore ACID o BASE. Nel caso di database BASE (MongoDB o altri), infatti, i provider cloud possono ottenere un’importante economia di scala abbattendo notevolmente i costi e riducendo di conseguenza le relative spese per il cliente finale (noi!).

In Azure ad esempio il costo di un SQL Server è piuttosto alto, mentre i costi di un Table Storage sono ridicoli per TB (!) di dati. Il costo estremamente ridotto potrebbe spingere diverse realtà quindi a convergere verso un modello di consistenza più rilassato per essere più competitivi sul mercato.

Posted in .net | 1 Comment »

Automapper: Ignore All Not Mapped Properties

Posted by Ricibald on 19th July 2013

Normally in automapper you’ll write something like this:

Mapper.CreateMap<MoveEntity, MoveEntityDto>()
       .ForMember(dest => dest.PrimaryOriginTransferee, opt => opt.Ignore())
       .ForMember(dest => dest.PrimaryDestinationTransferee, opt => opt.Ignore())
       .ForMember(dest => dest.Customer, opt => opt.Ignore())
       .ForMember(dest => dest.DestinationAddress, opt => opt.Ignore())
       .ForMember(dest => dest.OriginAddress, opt => opt.Ignore())
       .ForMember(dest => dest.Order, opt => opt.Ignore())
       .ForMember(dest => dest.Shipment, opt => opt.Ignore())
       .ForMember(dest => dest.SourceSystemName, opt => opt.Ignore());

To ignore all not-mapped properties just use this extension method:

Mapper.CreateMap<MoveEntity, MoveEntityDto>()
       .IgnoreAllNonExisting();

Here the source code:

    public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        var sourceType = typeof(TSource);
        var destinationType = typeof(TDestination);
        var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType.Equals(sourceType) && x.DestinationType.Equals(destinationType));
        foreach (var property in existingMaps.GetUnmappedPropertyNames())
        {
            expression.ForMember(property, opt => opt.Ignore());
        }
        return expression;
    }

Posted in .net | 2 Comments »

Page Doesn’t Postback After Validation Error

Posted by Ricibald on 27th June 2013

If there is a validation error, web controls that don’t need to repeat validation don’t postback on first attempt but postback on second attempt.

The reason (thanks to Lions Den) is that after running Page_ClientValidate ASP.NET runs another built in function ValidatorCommonOnSubmit. Within Page_ClientValidate, Page_BlockSubmit is set based on the validation. The postback is then blocked in ValidatorCommonOnSubmit if Page_BlockSubmit is true. No matter what, at the end of the function Page_BlockSubmit is always reset back to false. If a page does a postback without running any validation and Page_BlockSubmit has not been reset to false, the partial postback will be blocked.

First solution: reset Page_BlockSubmit

In this solution we just reset Page_BlockSubmit. This is the simplest solution and doesn’t have collateral effects. But you need to set this on every case you’ll need it.

function DoValidation() {
   var validated = Page_ClientValidate('ValidationGroup');

   // THIS IS THE CHANGE: REMOVE THE FLAG
   Page_BlockSubmit = false;

   return validated;
}

Second solution: never block postback

In this solution we redefine the code of ValidatorCommonOnSubmit returning always true (just make sure to copy and paste the same ASP.NET implementation):

function ValidatorCommonOnSubmit() {
   Page_InvalidControlToBeFocused = null;
   var result = !Page_BlockSubmit;
   if ((typeof(window.event) != "undefined") && (window.event != null)) {
      window.event.returnValue = result;
   }
   Page_BlockSubmit = false;

   // THIS IS THE CHANGE: ALWAYS RETURN TRUE
   return true;
}

Posted in asp.net | No Comments »

Evoluzione dei test – Debugger, TDD, BDD, LINQPad

Posted by Ricibald on 24th June 2013

Uno strumento fondamentale per sviluppare in .NET si sta sempre più rilevando LINQPad.

Nonostante sia nato per analizzare le query prodotte da LINQ, di fatto lo strumento è molto di più: consente infatti di testare al volo porzioni di codice .NET.

Facciamo subito un esempio pratico. Immaginiamo di avere un codice 3-layer con diversi componenti remoti che interagiscono tra di loro mediante protocolli più o meno complessi. Uno di questi componenti deve validare l’indirizzo mail che arriva in input da uno degli altri componenti. Il codice che validerà la mail è il seguente:

Regex.Match(emailAddress, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$");

Immaginiamo di voler testare questo codice. Come faresti?

Come verifichiamo il nostro codice? Vecchio stile…

In uno sviluppo classico (errato) si procede in questo modo:

  1. si scrive il codice fino a ottenere l’elaborato finale
  2. si testa il codice facendo girare tutti i vari componenti e incrociando le dita… se va tutto bene, bene… sennò si collega il debugger di VS e si passa per giorni e giorni di debugging…

Questo approccio ha tanti limiti tra cui: dipendenza da componenti esterni, tempi tecnici lunghi, non ripetibilità automatica del test.

In uno sviluppo più evoluto (TDD) si farebbe uso di unit test come segue:

  1. si scrive prima il metodo di test che verifica che a fronte di mail corrette non abbiamo errori e che viceversa a fronte di mail errate avremo eccezioni
    • il test dovrà essere isolato e atomico iniettando mock o, meglio, stub
  2. si scrive il minimo codice possibile per far compilare il codice
  3. si esegue il test, che deve fallire (il comportamento non è ancora implementato)
  4. si scrive l’implementazione vera e propria del test
  5. si riesegue il test, che stavolta dovrà andare bene

Questo approccio corregge i limiti sopra esposti consentendo una ripetibilità dei test e un isolamento dei componenti.

Gli unit test sono la risposta allora… Quale è il problema?

Gli unit test sono il modo di procedere corretto ma richiede una competenza di alto livello:

  • bisogna conoscere framework di dependency injection, unit test, mocking
  • bisogna conoscere bene i confini di un test: non bisogna testare né troppo né poco
  • gli input dei test devono essere significativi per verificare i “casi limite”
  • richiedono molto rigore: a ogni bug rilevato bisognerebbe creare il test corrispondente e fixarlo
  • bisogna saper isolare molto bene i test e rifattorizzarli nei punti variabili per evitare che una modifica nel codice comporti una modifica a cascata di tutti i test collegati

Oltre alle competenze richieste, i test di fatto aumentano comunque il lavoro complessivo:

  • bisogna scrivere test e mock per tutti gli input significativi
  • bisogna rifattorizzare i test e mantenerli nel tempo a fronte di cambiamenti anche strutturali

Non sto dicendo non bisogna fare unit test, anzi. Il problema è che una competenza simile è difficile trovarla ed è complicato trovare un progetto con un contesto culturale favorevole a un approccio di questo tipo.

Gli unit test sono troppo complessi…? Allora testiamo solo gli use case…

Allora era giusto il precedente approccio? Torniamo al debbugger con VS? Non credo sia corretto tornare nella propria zona di comfort, l’ideale sarebbe crearsi la competenza per perseguire gli unit test “puri”. Ma una via di mezzo potrebbe comunque esistere.

Si può semplificare e non scrivere 100 test in isolamento arrivando a una copertura del 100% del codice testato, bensì scrivere solo test “integrati” che vadano a verificare end-to-end il comportamento del codice integrato. Questo assomiglia più al BDD (Behavior-driven development) e presenta i seguenti vantaggi:

  • si verificano le porzioni di codice che effettivamente verranno eseguite nei vari casi d’uso
  • non richiede un’infrastruttura di stub/mock: si possono anche non conoscere framework di dependency injection e mocking

Il problema è che comunque i test devono essere atomici: l’esito non deve essere influenzato da altri test. Significa che siamo costretti a creare nel db e/o componenti dipendenti tutte le precondizioni che servono per avere una situazione “pulita” da testare.

Il BDD è ottimo per avere test che verifichino il giro completo del codice in modo ripetibile e quindi automatizzabile. Ma il BDD non è efficace per il nostro semplice caso di test di esempio. Se vogliamo solo testare se l’espressione regolare è valida per un’indirizzo email dovremmo porci in troppe precondizioni per poi fare semplici asserzioni.

 Gli use case sono troppo complessi…? Compilo ed eseguo al volo…

Un approccio estremamente più semplice e immediato per verificare il codice potrebbe essere quello di crearsi una Console Application, copiare/incollare il codice e testare il codice dentro la console.

Questo ha però dei tempi tecnici necessari come creare la solution, compilare e andare in debug. Questo comporta una risposta psicologica negativa: il test si fa solo quando serve veramente. Il vantaggio ci sarebbe ma per esperienza, nonostante sia il modo più immediato, nessuno lo fa: ci sarà o no un motivo…?

Un test al volo è troppo macchinoso…? Viene in aiuto LINQPad!

Siamo finalmente arrivati al nostro incredibile “silver bullet”. LINQPad, oltre a verificare le query LINQ generate, consente di ispezionare il risultato di un qualunque snippet di codice .NET compilandolo “al volo” tramite CSharpCodeProvider.

Lo screenshot seguente mostra, nello scenario di test presentato all’inizio, un test “al volo” della validazione della mail in questione:

Expression

Abbiamo quindi “cablato” il test verificando che con email “test@gmail.com” il metodo Regex.Match restituisce Success == true. Possiamo inoltre navigare il risultato: ad esempio possiamo ispezionare la collezione Groups, ottenendo i relativi gruppi in modo molto semplice:
Navigate

Volendo potremmo immergere il test in più input significativi creando una piccola procedura come segue:
Program

Se il codice che stiamo copiando o incollando fa uso di librerie esterne (create da noi o scaricate) possiamo includerle in modo da farle riconoscere dal compilatore.

Nell’esempio precedente si nota l’extension method Dump che consente di mostrare a video il contenuto di una qualunque struttura consentone una semplice navigazione tra strutture complesse. Prendiamo come esempio un Dictionary… in LINQPad tramite Dump lo visualizzereste in questo modo, direi piuttosto comodo, no…??
Dictionary

Uso di LINQPad

LINQPad si presta benissimo ai seguenti usi:

  • test al volo di nostro codice copiato/incollato da VS
  • test al volo di API .NET come LINQ, PLINQ, RX, …
  • test degli stati intermedi del chaining (qui un approfondimento)
  • apprendimento tramite esperimenti di librerie del framework .NET o di terze parti scaricate tramite NuGet
  • ispezione e navigazione di strutture e relazioni
  • navigazione del database senza passare per continue JOIN
  • analisi del database da un più alto livello di astrazione
    • grazie a ORM supportati come EF
    • grazie a provider supportati come MSCRM, Sharepoint, Azure, DataMarket, OData, StreamInsight, …
  • utility di uso comune tramite extension method (qui alcuni extension method di terze parti)
    • query LINQ che generano file CSV
    • query LINQ che generano SQL complessi da inviare via mail

Prossimi passi…??

Questi i prossimi passi per guadagnare la cintura marrone in LINQPad:

  1. Scarica LINQPad
  2. Scrivi il tuo codice normalmente in Visual Studio
  3. Appena terminato un metodo, anche semplice, copialo in LINQPad
    1. Se il tuo metodo fa uso di strutture esterne copiale direttamente o, se troppe, includi la dll
  4. Esegui su un input di esempio e ispeziona tramite Dump che tutto sia ok
    1. Se necessario verifica anche i tempi di esecuzione riportati
    2. Se necessario salva il codice per un futuro uso nella cartella “My Queries
  5. Rendila un’abitudine nel tuo lavoro quotidiano

Se veramente diventa un’abitudine eviterai il 90% degli errori, abbattendo ore noiose e degradanti di debugging…

Questi i passi invece per guadagnare la cintura nera in LINQPad:

  1. nella posizione dove di solito apri SQL Server Management Studio, rimpiazzalo con LINQPad
  2. apri sempre LINQPad al suo posto e fai le query solo utilizzando LINQ
  3. dopo un periodo di iniziale lentezza rispetto a SSMS scoprine i vantaggi
  4. non eseguire più join… limitati a seguire i link
  5. non ripetere più le stesse query… crea extension method riusabili
  6. non ipotizzare le query LINQ prodotte… verifica il SQL e prendine consapevolezza

Sperimenta nuove API con LINQPad. Pronti per lo step??

Posted in .net, test | No Comments »

WCF Client: “Using” best practices

Posted by Ricibald on 15th March 2013

According to this official Microsoft link you should not use “using” to dispose your wcf client.

Instead use this extension method:

namespace System.ServiceModel
{
    public static class Extensions
    {
        /// <summary>
        /// Release safely your WCF Client
        /// </summary>
        public static void UsingSafe<T>(this T client, Action<T> work)
            where T : ICommunicationObject
        {
            try
            {
                work(client);
            }
            finally
            {
                try
                {
                    client.Close();
                }
                catch (CommunicationException)
                {
                    client.Abort();
                }
                catch (TimeoutException)
                {
                    client.Abort();
                }
                catch (Exception)
                {
                    client.Abort();
                    throw;
                }
            }
        }
    }
}

Usage:

new SomeClient().UsingSafe(channel => {
    channel.Login(username, password);
});

Posted in .net, wcf | No Comments »