Protokollierung aller Datenänderungen mit Entity Framework

entity-framework logging

Frage

Der Kunde muss jede Datenänderung in einer Protokolltabelle mit dem tatsächlichen Benutzer protokollieren, der die Änderung vorgenommen hat. Die Anwendung verwendet einen SQL-Benutzer für den Zugriff auf die Datenbank. Wir müssen jedoch die "echte" Benutzer-ID protokollieren.

Wir können dies in t-sql tun, indem wir für jede Einfügung und Aktualisierung von Tabellen Trigger schreiben und die Benutzer-ID mit context_info speichern. Wir haben die Benutzer-ID an eine gespeicherte Prozedur übergeben, die Benutzer-ID in der Kontextinfo gespeichert, und der Auslöser könnte diese Informationen verwenden, um Protokollzeilen in die Protokolltabelle zu schreiben.

Ich kann den Ort oder Weg nicht finden, wo oder wie ich mit EF etwas Ähnliches machen kann. Das Hauptziel ist also: Wenn ich die Daten über EF ändere, möchte ich die genaue Datenänderung halbautomatisch in einer Tabelle protokollieren (deshalb möchte ich nicht für jedes Feld vorher nach Änderungen suchen) Objekt speichern). Wir verwenden EntitySQL.

Leider müssen wir uns an SQL 2000 halten, so dass die in SQL2008 eingeführte Datenänderungserfassung keine Option ist (aber vielleicht ist das auch nicht der richtige Weg für uns).

Irgendwelche Ideen, Links oder Ansatzpunkte?

[Bearbeiten] Einige Anmerkungen: Mit ObjectContext.SavingChanges eventhandler kann ich den Punkt ermitteln, an dem ich die SQL-Anweisung einfügen kann, um die contextinfo zu initialisieren. Ich kann jedoch nicht die EF und die Standard-SQL mischen. Ich kann also die EntityConnection erhalten, aber keine T-SQL-Anweisung ausführen. Oder ich kann die Verbindungszeichenfolge der EntityConnection abrufen und eine darauf basierende SqlConnection erstellen. Dies ist jedoch eine andere Verbindung, sodass die contextinfo die durch die EF vorgenommene Sicherung nicht beeinflusst.

Ich habe im SavingChanges-Handler Folgendes versucht:

testEntities te = (testEntities)sender;
DbConnection dc = te.Connection;
DbCommand dcc = dc.CreateCommand();
dcc.CommandType = CommandType.StoredProcedure;
DbParameter dp = new EntityParameter();
dp.ParameterName = "userid";
dp.Value = textBox1.Text;
dcc.CommandText = "userinit";
dcc.Parameters.Add(dp);
dcc.ExecuteNonQuery();

Fehler: Der Wert von EntityCommand.CommandText ist für einen StoredProcedure-Befehl nicht gültig. Dasselbe gilt für SqlParameter anstelle von EntityParameter: SqlParameter kann nicht verwendet werden.

StringBuilder cStr = new StringBuilder("declare @tx char(50); set @tx='");
cStr.Append(textBox1.Text);
cStr.Append("'; declare @m binary(128); set @m = cast(@tx as binary(128)); set context_info @m;");

testEntities te = (testEntities)sender;
DbConnection dc = te.Connection;
DbCommand dcc = dc.CreateCommand();
dcc.CommandType = CommandType.Text;
dcc.CommandText = cStr.ToString();
dcc.ExecuteNonQuery();

Fehler: Die Abfragesyntax ist nicht gültig.

Hier bin ich also festgefahren, um eine Brücke zwischen Entity Framework und ADO.NET zu schaffen. Wenn ich es schaffen kann, werde ich einen Proof of Concept veröffentlichen.

Akzeptierte Antwort

Wie wäre es mit dem Kontext? Änderungen speichern ?


Beliebte Antwort

Danke, dass Sie mich in die richtige Richtung weisen. In meinem Fall muss ich jedoch bei Kontextanweisungen auch die Kontextinformationen festlegen, da ich Ansichten abfrage, die die Kontextinformationen verwenden, um die Sicherheit auf Zeilenebene nach Benutzer zu steuern.

Ich fand es am einfachsten, an das StateChanged-Ereignis der Verbindung anzuhängen und nur auf die Änderung von Nicht-Öffnen zu Öffnen zu achten. Dann rufe ich das proc auf, das den Kontext setzt, und es funktioniert jedes Mal, auch wenn EF sich dafür entscheidet, die Verbindung zurückzusetzen.

private int _contextUserId;

public void SomeMethod()
{
    var db = new MyEntities();
    db.Connection.StateChange += this.Connection_StateChange;
    this._contextUserId = theCurrentUserId;

    // whatever else you want to do
}

private void Connection_StateChange(object sender, StateChangeEventArgs e)
{
    // only do this when we first open the connection
    if (e.OriginalState == ConnectionState.Open ||
        e.CurrentState != ConnectionState.Open)
        return;

    // use the existing open connection to set the context info
    var connection = ((EntityConnection) sender).StoreConnection;
    var command = connection.CreateCommand();
    command.CommandText = "proc_ContextInfoSet";
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("ContextUserID", this._contextUserId));
    command.ExecuteNonQuery();
}


Related

Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow