Journalisation de chaque changement de données avec Entity Framework

entity-framework logging

Question

Un client a besoin de consigner chaque modification de données dans une table de journalisation avec l'utilisateur réel qui a effectué la modification. L'application utilise un utilisateur SQL pour accéder à la base de données, mais nous devons enregistrer le "vrai" ID utilisateur.

Nous pouvons faire cela dans t-sql en écrivant des déclencheurs pour chaque insertion et mise à jour de table, et en utilisant context_info pour stocker l'identifiant de l'utilisateur. Nous avons transmis l'ID utilisateur à une procédure stockée, enregistré dans les informations contextuelles, et le déclencheur pourrait utiliser ces informations pour écrire des lignes de journal dans la table de journal.

Je n'arrive pas à trouver l'endroit ou le chemin où ou comment puis-je faire quelque chose de similaire en utilisant EF. L’objectif principal est donc: si j’effectue une modification des données via EF, je souhaite consigner la modification exacte des données dans une table de manière semi-automatique (je ne souhaite donc pas vérifier la modification de tous les champs auparavant). sauver l'objet). Nous utilisons EntitySQL.

Malheureusement, nous devons nous en tenir à SQL 2000 pour que la capture des modifications de données introduite dans SQL2008 ne soit pas une option (mais peut-être que ce n'est pas non plus la bonne façon pour nous).

Des idées, des liens ou des points de départ?

[Modifier] Quelques notes: en utilisant ObjectContext.SavingChanges, un gestionnaire d’événements, je peux obtenir le point où je peux injecter l’instruction SQL pour initialiser le contextinfo. Cependant, je ne peux pas mélanger l'EF et le SQL standard. Je peux donc obtenir EntityConnection mais je ne peux pas exécuter une instruction T-SQL en l'utilisant. Ou bien je peux obtenir la chaîne de connexion de EntityConnection et créer une connexion SqlConnection basée sur celle-ci, mais il s'agira d'une connexion différente, de sorte que le contextinfo n'affectera pas la sauvegarde effectuée par l'EF.

J'ai essayé ce qui suit dans le gestionnaire SavingChanges:

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();

Erreur: la valeur de EntityCommand.CommandText n'est pas valide pour une commande StoredProcedure. La même chose avec SqlParameter au lieu de EntityParameter: SqlParameter ne peut pas être utilisé.

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();

Erreur: la syntaxe de la requête n'est pas valide.

Je suis donc coincé pour créer un pont entre Entity Framework et ADO.NET. Si je peux le faire fonctionner, je posterai une preuve de concept.

Réponse acceptée

Que diriez-vous de la gestion du contexte. SavingChanges ?


Réponse populaire

Merci de m'avoir orienté dans la bonne direction. Cependant, dans mon cas, je dois également définir les informations de contexte lors de la sélection d'instructions, car j'interroge les vues qui utilisent les informations de contexte pour contrôler la sécurité au niveau de la ligne par utilisateur.

J'ai trouvé plus facile d'attacher à l'événement StateChanged de la connexion et de simplement regarder le changement de non-ouvert à ouvert. J'appelle ensuite le proc qui définit le contexte et cela fonctionne à chaque fois, même si EF décide de réinitialiser la connexion.

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

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow