The official Fatica Labs Blog! RSS 2.0
# Friday, September 03, 2010

Meglio, più in generale, non funziona più l’intellisense per qualsiasi documento XML per cui abbiamo uno ( o più.. ) XSD. Questo accade perchè Visual Studio trova più sorgenti per lo schema dichiarato nell’ XML, nel caso NHibernate abbiamo la seguente dichiarazione in testa:

<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="....." namespace="....." >

Se abbiamo nella solution, o nella directory %Program Files%/Microsoft Visual Studio XXX/Xml/Schemas  uno o più file che definiscono lo stesso namespace, in questo caso

<xs:schema targetNamespace="urn:nhibernate-mapping-2.2" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="urn:nhibernate-mapping-2.2" elementFormDefault="qualified" attributeFormDefault="unqualified">

Visual Studio trova un ambiguità per cui smette di fornire l’intellisense. La situazione si può risolvere facilmente senza disperare:

Basta cliccare sul documento XML da visual studio e dalla finestra proprietà editare la voce Schemas come mostrato qui sotto:

xmlschemas

Così facendo si apre una finestra in cui è possibile abilitare/disabilitare gli schema che visual studio intende usare:

xmlschema2

Deselezionando uno degli schemi ambigui tutto torna a funzionare. Ovviamente questo funziona per qualsiasi ambiguità di schema, non solo per NHibernate.

Friday, September 03, 2010 10:28:31 AM (GMT Daylight Time, UTC+01:00)  #    Comments [3] - Trackback
NHibernate | Programmin
# Wednesday, June 16, 2010

Probabilmente vi sarà capitato di dovere eseguire qualche azione all’interno del vostro software dipendentemente da trigger innescati da data e/o ora. Per esempio un certo processo non si scatena del week-end, oppure un altro processo deve partire solo a mezzanotte etc etc. Quando queste cose non siano attuabili esternamente con il task scheduler di sistema, sarebbe bello poter avere una libreria pronto uso in .NET. Ebbene questa libreria c’è, ed è il parser di espressioni Cron che trovate qui. Lungi dall’essere un concetto moderno, si tratta di un modo di esprimere range temporali tramite una stringa che arriva da Unix. Oggi chiamiamo una cosa simile “Domain Specific Language”, ma tant’è il concetto è il medesimo. Lungi anche dall’ essere originale il concetto in .NET, la libreria che propongo è una estrapolazione di codice dalla più completa e complessa libreria Quartz.NET, a sua volta un porting della libreria Java Quartz. Se non volete introdurre troppa complessità al vostro deploy aggiungendo una nuova libreria, potete “grabbare” le classi dalla dll di Cron.NET ( sono tre file… ) e compilarli con il vostro progetto, ed avrete a disposizione tutta la potenza delle espressioni Cron ;).

Già, ma come funzionano le espressioni Cron ? Facciamo un paio di esempi:

"* * 10-11 L * ?" = questo trigger è valido l’ultimo del mese dalle 10-11

"* * * ? * MON-FRI" = questo trigger è valido tutti i giorni, a tutte le ore, eccetto il week end.

Ok, questo da l’idea delle potezialità della cosa, ma ovviamente serivirebbe una più estesa…

Documentazione

Una espressione Cron è composta da 6 campi obbligatori, più uno opzionale. Il significato dei campi è schematizzato nella tabella qui sotto:

Campo   Valori permessi   Caratteri speciali ammessi
Secondi   0-59   , - /
Minuti   0-59   , - /
Ore   0-23   , - /
Giorno del mese   1-31   , - ? / L W C
Mese   1-12 or JAN-DEC   , - /
Giorno della settimana   1-7 or SUN-SAT   , - ? / L #
Anno ( Opzionale )   vuoto, 1970-2099   , - /

 

Il carattere jolly ‘*’ può sempre essere usato con il significato di “tutti i valori”, per esempio se metto ‘*’ nel campo minuti significa tutti i minuti.

Il carattere ‘?’ può essere usato solo come Giorno del mese o giorno della settimana. Significa “nessun valore in particolare” e serve quando devo fissare l’altro. Se per esempio fisso il giorno della settimana metterò ‘?’ nel giorno del mese, e viceversa.

Il carattere ‘-‘ lo utilizzo quando voglio specificare un range: per esempio 10-12 nel campo ora significa dalle 10 a tutte le 12.

Uso invece la ‘,’ quando voglio specificare singolarmente più valori possibili: JAN,MAR significa il mese di Gennaio e di Marzo, ma non Febbraio ( e, ovviamente, non tutti gli altri ).

Per specificare dei periodi utilizzo ‘/’: per esempio se metto 0/15 nel campo secondi, significa “ogni 15 secondi”: sarà attivo il trigger quindi ai secondi 15,30,45 etc etc.

Il carattere speciale ‘L’ significa ultimo. Nel campo mese significa semplicemente l’ultimo giorno del mese (30/31/28 o 29 ) in modo concorde con il mese / anno bisestile. Nel campo settimana significa semplicemente 7 ( cioè Sabato ), ma se preceduto da un numero significa “l’ultimo xxxx del mese”. Per esempio se scrivo 6L significa l’ultimo Venerdì del mese.

Il carettere ‘W’ può essere specificato solo nel campo giorno del mese, preceduto da un solo carattere, e significa “il giorno lavorativo più vicino a”. Per esempio se scrivo 1W significa il giorno lavorativo più vicino al primo del mese. Se scrivo LW significa l’ultimo giorno lavorativo del mese.

Per ultimo il carattere ‘#’, utilizzabile solo nel giorno della settimana, indica l’ennesimo xxxx del mese. Per esempio, se mi interessa il terzo venerdì del mese, scriverò 6#3.

Per utilizzare una cron expression basta creare un istanza dell’ogegtto CronExpression, passando la stringa sul costruttore. Ecco un esempio:

   1:          [Test]
   2:          public void LastOfTheMonth()
   3:          {
   4:              var exp = new CronExpression("* * * L * ?");
   5:              Assert.IsTrue(exp.IsSatisfiedBy(new DateTime(2000, 1, 31)));
   6:              Assert.IsTrue(exp.IsSatisfiedBy(new DateTime(2000, 2, 29)));
   7:              Assert.IsFalse(exp.IsSatisfiedBy(new DateTime(2000, 2, 28)));
   8:          }
Wednesday, June 16, 2010 4:52:39 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Cron | Programmin | Recipes
# Thursday, May 20, 2010

Può succedere di dover specificare il nome di un assembly completo, magari in un file di configurazione dell’applicativo. Sfortunatamente non si può tagliare ed incollare la stringa dalla GAC, e bisogna copiare ed incollare i vari pezzi dalla finestra property. Ecco quindi un piccolo tool che mostra tutto il contenuto della GAC e ci permette di copiare l’assembly full name. Eccolo all’opera:

gac

Si accettano complimenti per la meravigliosa interfaccia utente ;).

Nondimeno importante segnalare lo snapshot di codice che consente con facilità di maneggiare la GAC run-time.

Thursday, May 20, 2010 11:48:35 AM (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin | Recipes
# Friday, May 14, 2010

Un problema veramente divertente: collegandomi ad Oracle Express, ma probabilmente lo stesso accade con l’edizione ufficiale, la connessione mi falliva con:


Oracle.DataAccess.Client.OracleException : ORA-12154: TNS: impossibile risolvere l'identificativo di connessione specificato

 

ovvero

 

TNS:could not resolve service name

sulla stessa macchina un’altra applicazione, con la medesima connection string funzionava perfettamente. Ora l’applicazione non funzionante era un test con NUnit, e sulle macchine a 64 bit il setup installa in ProgramFiles( x86 ). Pare che il client Oracle faccia qualche considerazione sulla directory in cui si trova il chiamante, e questo fallisca se ci sono dei caratteri strani ( secondo Oracle ) come le parentesi.Spostando NUnit in un percorso senza parentesi nel nome, tutto funziona. Indagando sul web, ho trovato una conferma al problema ivi descritto. Ho perso la mattinata appresso ad una stupidaggine :-~.

Friday, May 14, 2010 1:18:52 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin
# Tuesday, April 27, 2010

stringtemplate Una libreria che ho utilizzato più volte, ma che è secondo me misconosciuta è ANTLR StringTemplate. E’ parte di un progetto molto più grande ed importante, ovvero il generatore di linguaggi ANTLR, ma qui ci interessa la libreria di templating.

bene: che cosa è ? E’ un template engine, ovvero un componente che, data qualche forma di metadata mi produce un documento di testo il cui contenuto è in qualche modo pilotato dal metadata. Semplice? Forse un esempio è meglio:

Supponiamo di avere un oggetto .NET rappresentante una carrello della spesa di un sito web. Questo rappresenta il nostro metadata. Ora supponiamo che vogliamo scrivere una mail riassuntiva, rappresentante il contenuto del carrello. Ovviamente dovrebbe essere facile modifcare il corpo di questa mail, la sua formattazione, tramite una semplice configurazione. Vediamo come potrebbe essere l’oggetto carrello in questione:

   1: public class Basket
   2: {
   3:     public Basket ()
   4:     {
   5:         BasketItems = new List<BasketItem>();
   6:     }
   7:     public string UserName { get; set; }
   8:     public string UserSurname { get; set; }
   9:     public string UserEmail { get; set; }
  10:     public IList<BasketItem> BasketItems { get; set; }
  11: }
  12:  
  13: public class BasketItem
  14: {
  15:     public string Code { get; set; }
  16:     public string Description { get; set; }
  17:     public int Count { get; set; }
  18:     public decimal UnitPrice { get; set; }
  19:     public decimal Price { get { return UnitPrice * Count; } }
  20: }

Bene, ora scriviamo il template della mail, nel linguaggio di StringTemplate:

   1: Dear Mr $Basket.UserName$,$Basket.UserSurname$
   2:  
   3: Your order contains:
   4:  
   5: Item #    Description        Qty    Unit Cost    Total Cost
   6: $Basket.BasketItems:
   7: {
   8: $it.Code$    $it.Description$        $it.Count$    $it.UnitPrice$        $it.Price$ }
   9: $
  10:  
  11: ______________________________
  12: Thanks for ordering @ ACME.ORG

Da notare, se siete abituati ad altri template engine ( ASP è uno di questi, ma anche T4 ) noterete che questi quando lavorano con collezioni vi fanno scrivere qualcosa come <% for….. %>, facendovi fare il template in modo procedurale. StringTemplate usa invece un approccio dichiarativo $collection: {}$ tra le graffe va il template da ripeter per ogni item della collection. l’elemento corrente è rappresentato da $it$.

Ora bisogna procurere i binari del porting c# di StringTemplate. Mettete come reference nel vostro progetto le dll StringTemplate, ma ricordatevi che per il deploy servono tutte e tre le dll! Fatto questo, per processare il template basta scrivere:

   1: public void TryTemplate()
   2: {
   3:     using( var st =  new StreamReader("mailtemplate.txt") )
   4:     {
   5:         StringTemplate processor = new StringTemplate(st.ReadToEnd());
   6:         //GetBasket ritorna un basket di esempio
   7:         processor.SetAttribute("Basket", GetBasket());
   8:         //ToString processa il template con i dati...
   9:         Console.WriteLine(processor.ToString()); 
  10:     }
  11: }

Per ottenere l’output seguente:

   1: Dear Mr Will,Coyote
   2:  
   3: Your order contains:
   4:  
   5: Item #    Description        Qty    Unit Cost    Total Cost
   6:  
   7: 1    T.N.T Stick        200    0,45        90,00 
   8: 2    Chain Gun        1    1450        1450 
   9: 3    Rockets        60    200        12000 
  10:  
  11: ______________________________
  12: Thanks for ordering @ ACME.ORG

Semplice, e molto efficace. Questo è un esempio base, collection e parametri a me sono sempre bastati, ma la libreria in se è molto più potente: basta leggersi la documentazione. So benissimo che a qualcuno potrebbe venire voglia, di fronte ad una simile problematica, di scriversi una propria versione, ma… okkio, questa è una delle classiche cose semplici che vi portano via, a farla male, una giornata, ma a farla bene… beh, prendete quella già fatta perchè:

  • è collaudata - è usata come back-end di ANTLR, un genaratore di compilatori di successo-
  • è potente – gestisce collection, sostituzioni, inclusioni, join etc etc
  • è open source, ed è in C#.
Tuesday, April 27, 2010 10:31:08 PM (GMT Daylight Time, UTC+01:00)  #    Comments [3] - Trackback
Programmin
# Wednesday, April 21, 2010

E’ da un po’ che in una certa realtà lo ripeto, ma non avevo mai trovato qualcosa di ufficiale a conforto. Adesso l’ho trovato.

In pratica vediamo perchè, scrivere l’interfaccia ICustomer sull’entità Customer sia sempre un errore. Sempre. Non in qualche caso. Perchè un entità non ha comportamenti. Un entità, per esempio “Customer” ritorna sempre la stessa cosa quando le si chiede, ad esempio, Name. Non c’è un algoritmo od una strategy che possa abbellire o rendere più intelligente il comportamento di Name. E’ poi interessante vedere come larchitettura diventa semplicemente oscena quando ICustomer ha per esempio un IAddress o qualsiasi altra reference…

Poichè è anche considerato un antipattern il Domain Model anemico ( cioè quello composto da sole entità  senza comportamenti) chi si ferma a leggere le prime righe di tutti gli articoli potrebbe pensare che piazzare su una bella interfaccia per ogni entità sia la soluzione. Questo non è però vero: vanno messi “dietro ad un interfaccia” i comportamenti che ha senso pensare che possano variare, ed eventualmente le entità potranno implementare una o più di queste interfacce di behavior. Per esempio Customer potrebbe implementare ICanPlaceHorder, e la logica con cui si piazza l’ordine potrebbe cambiare, ma le entità in gioco sono concrete.

Un interessante find storico sulla situazione è un caso pratico di Prima & Dopo.

Wednesday, April 21, 2010 1:13:42 PM (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
Programmin
# Saturday, April 17, 2010

Uno scenario di possibile utilizzo di RemotingAppender è quando si voglia concentrare in un unico punto i log di più app domain hostati nello stesso processo. Infatti un app domain separato vede difatto un’altra configurazione di log4net e, se si volesse per esempio usare un appender su file, ci sarebbe una violazione di condivisione. Per cui RemotingAppender torna utile, consentendo di ribaltare il log di tutti gli app domain sul programma principale. Per prima cosa bisogna mettere il programma principale in grado di ricevere le chiamate di log via remoting. Supponiamo che MyLauncher sia la classe che avvia un processo in un AppDomain separato, potremmo scrivere:

class MyLauncher:MarshalByRefObject,log4net.Appender.RemotingAppender.IRemoteLoggingSink
{

E’ necessario che la classe derivi da MarshalByRef object, in quanto ne esporremo l’istanza via remoting. Deve altresì implementare IRemoteLoggingSink , per fungere da target per i messaggi di log4net.

A questo punto bisogna registrare il canale e l’istanza dell’oggetto atto a fare il sink dei messaggi:

   1:  private void PrepareRemotingLoggerListener()
   2:  {
   3:              var channel = new IpcChannel("log4net"+Guid.NewGuid().ToString());
   4:   
   5:              ChannelServices.RegisterChannel(channel, false);
   6:              var oref = RemotingServices.Marshal(this, null);
   7:              AppenderURI = channel.GetUrlsForUri(oref.URI)[0];
   8:  }

 

Supponiamo di chiamare il codice di sopra per ogni hosting di app domain. Il canale viene battezzato con una componente casuale, in modo da non entrare in conflitto con altre istanze dello stesso programma, o all’interno del medesimo processo se vi fossero più app domain esterni in esecuzione. Per registrare con remoting un istanza già creata si è usato RemotingServices.Marshal(---). Il codice memorizza in una proprietà AppenderURI: questo sarà l’indirizzo da usarsi con remoting appender per definire la proprietà Sink.In pratica è l’indirizzo remoting dell’oggetto “sink” dei messaggi.

Implementare IRemoteLoggingSink è facilissimo:

   1:          #region IRemoteLoggingSink Members
   2:   
   3:          public void LogEvents(log4net.Core.LoggingEvent[] events)
   4:          {
   5:              foreach (var le in events)
   6:                  logger.Logger.Log(le); // logger è un'istanza di 
//ILog nel programma principale, ottenuta con logManager.GetLogger...
   7:          }
   8:   
   9:          #endregion

In questo modo si ottiene un redirect di tutti i messaggi sul logger dell’applicazione principale, con filtri e appender come stabilito dalla configurazione dell’applicativo principale. I messaggi saranno accodati, quindi non ci saranno violazioni di condivisione anche con appender su file fisico.

Nell’app domain basterà configurare log4net in modo da utilizzare RemotingAppender. Il modo più facile è utilizzare BasicConfigurator:

   1:    BasicConfigurator.Configure(CreateAppender());
   2:    logger = LogManager.GetLogger("logger name");
 

CreateAppender si occuperà di creare il RemotingAppender in questo modo:

   1:          private IAppender CreateAppender()
   2:          {
   3:              RemotingAppender ra = new RemotingAppender();
   4:              ra.Layout = new log4net.Layout.SimpleLayout();
   5:              ra.Sink = LoggerURI; // Indirizzo del "sink"
   6:              ra.BufferSize = 512;
   7:              ra.Lossy = false;
   8:              ra.ActivateOptions(); // Ricordarsi di chiamare !!!
   9:              return ra;
  10:          }

Ed il gioco è fatto. LoggerUri è l’indirizzo dell’oggetto di sink ( vedi AppenderURI di prima ). BufferSize è il numero di messaggi da accodare prima di fare effettivamente la chiamata al canale: per ragioni di performance è meglio non abbassare troppo la soglia dei messaggi da raggruppare.

Saturday, April 17, 2010 9:02:23 AM (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
log4net | Programmin
Archive
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Felice Pollano
Sign In
Statistics
Total Posts: 67
This Year: 41
This Month: 2
This Week: 0
Comments: 36
This blog visits
Locations of visitors to this page
All Content © 2010, Felice Pollano
DasBlog theme 'Business' created by Christoph De Baene (delarou) and modified by Felice Pollano