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