Updating common field with user email

2019-08-17 19:37发布

问题:

I am trying to find out what the authorized user is currently trying to save the data and record their email.

I have a basic setup in my dbcontext that does the update for timestamps but i cannot figure out how to access the user information:

    public override int SaveChanges()
{
    AddTimestamps();
    return base.SaveChanges();
}


public override async Task<int> SaveChangesAsync()
        {
            AddTimestamps();
            return await base.SaveChangesAsync();
        }

    private void AddTimestamps()
    {
        var entities = ChangeTracker.Entries()
            .Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);

        var currentUser = "dumm@dummy.com" // This i haven't been able to figure out how to retrieve

        foreach (var entity in entities)
        {
            if (entity.State == EntityState.Added)
            {
                ((BaseEntity)entity.Entity).DateCreated = DateTime.UtcNow;
                ((BaseEntity)entity.Entity).CreatedBy = currentUsername;
            }

            ((BaseEntity)entity.Entity).DateModified = DateTime.UtcNow;
            ((BaseEntity)entity.Entity).ModifiedBy = currentUsername;
        }
    }
}

Bit more of a background this is all built on ASP Core 2 and EF Core. The DBContext is located in my APIContoso project, which utilises the custom Identity Provider called IDPContoso which is built on ASP Core 2 and Identity Server4.

How can I obtain the users email within the DBContext so i can record it?

回答1:

I suggest you get the email from the identity. This way you don't need an extra call to the database. If you are using the email to login then User.Identity.Name contains the email (if mapped correctly). Otherwise add a claim that contains the email.

Now you'll need to inject the email into the model. I assume a seperated project for the model. In that case you cannot just inject IHttpContextAccessor. Otherwise you can simply inject IHttpContextAccessor and read the user info when the model is constructed.

The following code is for demonstration purposes only. In Startup.cs inject an object that contains the connectionInfo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(provider => new Database.ConnectionInfo
    {
        ConnectionString = provider.GetRequiredService<ApplicationSettings>().ConnectionString,
        User = provider.GetRequiredService<IHttpContextAccessor>().HttpContext.User?.Identity.Name
    });
    services.AddScoped<Model>();
}

Because this is scoped, every time a user connects, the user information is set. For anonymous users, User = null. Notice that the model itself is also scoped. The connectionInfo class:

public class ConnectionInfo
{
    public string ConnectionString { get; set; }
    public string User { get; set; }
}

Now in Model you have the following constructor:

private string _user { get; }

public Model(ConnectionInfo connection)
    : base(connection.ConnectionString)
{
    _user = connection.User;
}


private void AddTimestamps()
{
    var entities = ChangeTracker.Entries()
        .Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);

    var currentUser = _user;
}