Entity Framework 6.1 bringt eine ganze Menge neuer Interceptoren, mit denen der Entwickler seine eigenen Routinen einklinken kann. Einer davon ist der DbConnectionInterceptor, der Methoden anbietet, die zum Beispiel Entity Framework vor sowie nach dem Öffnen bzw. Schließen einer Datenbankverbindung ausführt.
Mit diesem Interceptor kann der Entwickler Entity Framework anweisen, im Zuge jedes Verbindungsaufbaus bestimmte Befehle an die Datenbank zu senden. Das könnten Befehle zum Initialisieren der Verbindung sein oder Befehle, die temporäre Tabellen erzeugen. Ob letzteres von guter Architektur zeugt, sei an dieser Stelle dahingestellt.
Um einen DbConnectionInterceptor bereitzustellen, implementiert der Entwickler das Interface IDbConnectionInterceptor. Dieses beinhaltet eine ganze Menge an Methoden. Jene, die nicht benötigt werden, erhalten einen leeren Body.
class CustomDbConnectionInterceptor : IDbConnectionInterceptor
{
[…]
}
Von Interesse für die eingangs erwähnte Aufgabenstellung ist die Methode Opened, welche – wie der Name schon vermuten lässt – Entity Framework nach dem Öffnen einer Datenbankverbindung ausführt. Das nachfolgende Listing zeigt eine Implementierung dieser Methode, welche eine temporäre Tabelle erstellt und in diese den Namen des aktuellen Benutzers einträgt. Somit können zum Beispiel Trigger oder Stored Procedures auf den aktuellen Benutzernamen zugreifen.
public void Opened(System.Data.Common.DbConnection connection, DbConnectionInterceptionContext interceptionContext)
{
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "create table #userInfo(userName varchar(255)); insert into #userInfo values(@userName);";
var param = cmd.CreateParameter();
param.Direction = System.Data.ParameterDirection.Input;
param.DbType = System.Data.DbType.String;
param.ParameterName = "userName";
param.Value = Thread.CurrentPrincipal.Identity.Name;
cmd.Parameters.Add(param);
var result = cmd.ExecuteNonQuery();
Debug.WriteLine(result);
}
}
Um einen DbConnectionInterceptor bei Entity Framework zu registrieren, übergibt der Entwickler eine Instanz davon innerhalb der verwendeten DbConfiguration an AddInterceptor.
class MyConfiguration: DbConfiguration
{
public MyConfiguration()
{
AddInterceptor(new CustomDbConnectionInterceptor());
}
}
Wer sich daran stößt, dass IDbConnectionInterceptor so viele zu implementierende Methoden mit sich bringt, kann auch von TransactionHandler ableiten. Diese abstrakte Basisklasse implementiert u. a. den IDbConnectionInterceptor mit leeren Methodenkörpern. Somit muss der Entwickler nur mehr die benötigten Methoden überschreiben. Allerdings zwingt der TransactionHandler seinen Derivaten auch eine Methode BuildDatabaseInitializationScript auf, welche jedoch im einfachsten Fall wie nachfolgend gezeigt implementiert werden kann.
class CustomTransactionHandler : TransactionHandler
{
public override string BuildDatabaseInitializationScript()
{
return null;
}
public override void Opened(
DbConnection connection,
DbConnectionInterceptionContext interceptionContext)
{
[…]
}
}
Zum Registrieren des TransactionHandlers verwendet der Entwickler innerhalb der zu verwendenden DbConfiguration die Methode SetTransactionHandler.
class MyConfiguration: DbConfiguration
{
public MyConfiguration()
{
SetTransactionHandler(
SqlProviderServices.ProviderInvariantName,
() => new CustomTransactionHandler());
}
}