Logging every data change with Entity Framework

后端 未结 8 989
醉酒成梦
醉酒成梦 2020-12-02 14:43

There is a need from a customer to log every data change to a logging table with the actual user who made the modification. The application is using one SQL user to access t

相关标签:
8条回答
  • 2020-12-02 15:21

    Simply force an execution of the SET CONTEXT_INFO by using your DbContext or ObjectContext:

    ...
    FileMoverContext context = new FileMoverContext();
    context.SetSessionContextInfo(Environment.UserName);
    ...
    context.SaveChanges();
    

    FileMoverContext inherits from DbContext and has a SetSessionContextInfo method. Here is what my SetSessionContextInfo(...) looks like:

    public bool SetSessionContextInfo(string infoValue)
    {
       try
       {
          if (infoValue == null)
             throw new ArgumentNullException("infoValue");
    
          string rawQuery =
                       @"DECLARE @temp varbinary(128)
                         SET @temp = CONVERT(varbinary(128), '";
    
          rawQuery = rawQuery + infoValue + @"');
                        SET CONTEXT_INFO @temp";
          this.Database.ExecuteSqlCommand(rawQuery);
    
          return true;
       }
       catch (Exception e)
       {
          return false;
       }
    }
    

    Now you just set up a database trigger which can access the CONTEXT_INFO() and set a database field using it.

    0 讨论(0)
  • 2020-12-02 15:24

    Thanks for pointing me in the right direction. However, in my case, I also need to set the context info when doing select statements, because I am querying views that use the context info to control row-level security by user.

    I found it easiest to attach to the StateChanged event of the connection and just watch for the change from not-open to open. Then I call the proc that sets context and it works every time, even if EF decides to reset the connection.

    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();
    }
    
    0 讨论(0)
提交回复
热议问题