Una delle nuove feature introdotte nella versione 6 di Entity Framework (attualmente in RC) è il supporto al logging dell’SQL generato dal runtime di EF6. A tal fine è sufficiente passare un opportuno delegate alla proporietà Log esposta da DbContext.Database. Per gli esempi riprendiamo lo scenario del post precedente.
Supponiamo di avere il seguente codice:
using (CarContext db = new CarContext())
{
System.Console.WriteLine("Cars in database : {0}", db.Cars.Count());
////Add a new car.
Car car = new Car()
{
Brand = "Alfa Romeo",
Model = "Giulietta"
};
db.Cars.Add(car);
db.SaveChanges();
System.Console.WriteLine("Cars in database : {0}", db.Cars.Count());
}
Che banalmente visualizza il numero di auto (Cars) presenti nel repository, ne aggiunge una e riesegue la conta delle auto registrate. Se volessimo analizzare l’SQL generato un ottimo strumento è sicuramete il SQL Profiler di SQL Server (che non dovrebbe mai mancare quando si lavora con un ORM ), ma se utilizziamo EF6, ed il nostro obiettivo è semplicemente “scoprire\loggare” l’SQL generato da EF, possiamo modificare il codice precedente aggiungendo dopo la definizione del DbContext, la riga seguente:
db.Database.Log = Console.Write;
Dove la proprietà Log è cosi’ definita:
public Action<string> Log { get; set; }
Eseguendo il codice della nostra applicazione dovremmo ottenere una “Console” simile alla seguente:
Di default il log è molto chiaro in quanto ci restituisce il testo SQL del comando, quando è stato eseguito, il tempo impiegato, il tipo di risultato, gli eventuali parametri ed il tipo corrispondente. Possiamo ovviamente cambiare il contenuto e la formattazione del log secondo le nostre esigenze (e non solo) come vedremo piu’ avanti. Per come è definita la proprietà Log, qualsiasi funzione che accetta una stringa puo’ essere utilizzata per scrivere il nostro log. Quindi potremmo utilizzare un metodo tipo il seguente:
public static void Log(string sql)
{
Console.WriteLine("------------------------------------");
Console.WriteLine("SQL LOG: {0} ", sql);
Console.WriteLine("------------------------------------");
}
oppure:
System.IO.StreamWriter streamWriter = new System.IO.StreamWriter (fileStream, System.Text.ASCIIEncoding.ASCII );
db.Database.Log = sql => streamWriter.Write(sql);
Per scrivere il log direttamente su file. Il concetto dovrebbe essere abbastanza chiaro. Vediamo come personalizzare la formattazione del log.
Database.Log non fa altro che utilizzare un ogetto DatabaseLogFormatter che a sua volta implementa le interfacce IDbCommandInterceptor e IDbInterceptor che sono rispettivamente l’interfaccia che permette di registrare un oggetto che possa ricevere notifiche da parte di Entity Framework quando un comando viene eseguito e l’interfaccia base per tutte le interfacce che permettono di fornire un Interception Point per differenti tipi di operazione.
Quindi, per creare il nostro “Formattatore Custom” non ci resta che ereditare dalla classe DatabaseLogFormatter ed eseguire l’override dei metodi che ci interessano, come nel caso seguente:
public class CustomLogFormatter
: DatabaseLogFormatter
{
public CustomLogFormatter(DbContext context, Action<string> writeAction)
: base(context, writeAction)
{
}
public override void LogCommand<TResult>(
DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Write(string.Format("{0}{1}", "===============================================", Environment.NewLine));
Write(string.Format(
"Context{2}{0}{2}Command{2}{1}{2}",
Context.GetType().Name,
command.CommandText.Replace(Environment.NewLine, ""),
Environment.NewLine));
Write(string.Format("{0}{1}", "===============================================", Environment.NewLine));
}
}
Dove, l’override del metodo LogCommand esegue la formattazione del comando prima che questo sia eseguito da Entity Framework. LogCommand a sua volta chiama eventualmente il metodo LogParameter per ogni parametro del comando, è necessario eseguire l’override di LogParameter per “customizzare” la formattazione del log dei paramateri. Infine, se vogliamo cambiare la formattazione del risultato, è necessario effettuare l’override di LogResult. Per interrompere l’esecuzione del comando, possiamo utilizzare il metodo :
interceptionContext.SuppressExecution()
Nel corpo dell’override di LogCommand. Per vedere in “funzione” il nostro “Formatter” è sufficiente registrare l’istanza di CustomLogFormatter tramite una classe derivata da DbConfiguration presente nello stesso assembly che contiene la definizione del DbContext:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseLogFormatter(
(context, writeAction) => new CustomLogFormatter(context, writeAction));
}
}
Eseguendo, otteniamo il “nostro” log:
Per approfondimenti:
Entity Framework Codeplex
One Unicorn: EF6 SQL Logging