I vari flavour del DataContext offrono la possibilità di agganciare un “logger” per vedere ad esempio le query prodotte, ad esempio con Linq2Sql avete una cosa del tipo:
using( var dc = new MyDataContext() )
{
dc.Log = Console.Out;
}
Questo, se avete un debugger attaccato, o siete in un’applicazione Console, scrive sulla Console (Output Window per il debugger) i messaggi di Log del DataContext, tra cui tutte le query T-Sql con i parametri etc etc…
Adesso supponiamo che vogliate scrivere quel log su un vostro sistema di Log, sia esso log4net, come nel mio caso, o il motore di Tracing di .net, poco importa quale sia la destinazione; in tutti i casi l’inghippo è che da una parte avete un TextWriter mentre dall’altra avete un metodo da chiamare tipicamente passando un messaggio sottoforma di stringa.
Per log4net ho trovato questa soluzione abbastanza complessa, a mio modo di vedere, che per inciso non funziona… ;-) o meglio se usate log4net in maniera basilare si, ma se cominciate a fare cose un po’ strane allora ciccia, non da errori ma non scrive nulla. All’inizio ci ho ragionato sopra un po’ cercando di capire quale fosse il problema… poi “blink” :-)
public sealed class ActionTextWriter : TextWriter
{
readonly Action<String> logger;
public ActionTextWriter( Action<String> logger )
{
Ensure.That( logger ).Named( "logger" ).IsNotNull();
this.logger = logger;
}
public override void Write( string value )
{
this.logger( value );
}
public override void Write( char[] buffer, int index, int count )
{
if( buffer == null || index < 0 || count < 0 || buffer.Length - index < count )
{
//let base class to throw exception
base.Write( buffer, index, count );
}
this.logger( new String( buffer, index, count ) );
}
Encoding _encoding;
public override Encoding Encoding
{
get
{
if( this._encoding == null )
{
this._encoding = new UnicodeEncoding( false, false );
}
return this._encoding;
}
}
}
ActionTextWriter, di una semplicità disarmante: altro non fa che intercettare le chiamate a “Write” del TextWriter e redirigerle sul delegato di tipo Action<String> che gli passate nel costruttore.
Per usarlo quindi, nel mio caso con log4net, mi limito a fare:
static readonly ILog logger = LogManager.GetLogger( typeof( MyRepository ) );
using( var dc = new MyDataContext( this.connectionString ) )
{
dc.Log = new ActionTextWriter( s => logger.Debug( s ) );
}
Semplice, funzionale e senza seg*e mentali di sorta ;-)
.m