User Information on Entities

Hello,

In lightswitch when the integrated security was used, all the entities in the database would get the following properties:

  • Created (datetime)

  • Createdby (nvarchar)

  • Modified (datetime)

  • ModifiedBy (datetime)

The system would automatically date stamp and record the username in these properties when a record was created or updated.

Is there a best practice for this in Radzen or am I missing something where this may be implemented automatically? I have created the properties on my entities, I'd just like to implement the code in one location across all entities rather than having to handle each one.

Thanks.

This isn't automatically done at the moment. As a first step we will prepare a sample application based on this approach and post it here.

1 Like

Thank you. That would be really helpful.

We were able to implement this with custom partial classes that implement a custom interface class IEntityAudit and override the context SaveChanges as per link (1) above.

 interface IEntityAudit
    {
        [Key]
        Guid Id { get; set; }
        DateTime CreatedAt { get; set; }
        DateTime LastUpdatedAt { get; set; }  
        [StringLength(20)]
        string LastUpdatedBy { get; set; }             
    }
2 Likes

We were able to implement this with custom partial classes that implement a custom interface class IEntityAudit and override the context SaveChanges as per link (1) above.

Thank you. I kinda get how this will be implemented and hope to see the example from Atanas to clarify it.

I do have another question though. If you override the SaveChanges function on the context and update the data source by inferring from schema again, will it overwrite any code you implement on the context?

Thanks.

It will not if you use a partial class as it lives in a different file.

Thanks Atanas, my basic implementation of what I've learnt today seems to fail to expose the username.

I have implemented the partial class for overriding the Saving function and can access the properties to update the modified and creation dates but I can't seem to figure how to expose the username for the CreatedBy and ModifiedBy properties.

I look forward to your demo. Thanks.

This tutorial show how to get the current user name.

1 Like

Yeah I'd seen that but in the SaveChanges function I can't seem to figure out how to access the ```
HttpContext. I can see how that would work from the controller but I was trying to handle all this from that override function.

You are right. There is no easy way to get that in the EF context class at the moment. Will have to inject it during code generation. Right now you can get it only from the Controller classes. This would allow you to set the CreatedBy/UpdatedBy fields but you will have to create a partial class for every controller which is less than ideal. We just have to release a new Radzen version that takes care of this.

@mumfie, how did you manage to pass in the username?

We just released Radzen 2.12.1 which has the needed changes. Also we released the AuditTrail sample application.

There are two custom files that you need to replicate in your application - server\Startup.Audit.cs and server\data\AuditTrailDbContext.Audit.cs (you should create a partial class for the DB context that Radzen has generated for your database).

The actual logging is done in the Audit method. You can rename the properties that the sample is using to match your existing DB schema, perform additional logging etc:

public static void Audit(EntityEntry entityEntry, string userName)
{
     var type = entityEntry.Entity.GetType();
     if (entityEntry.State == EntityState.Added)
     {
          SetProperty(type, entityEntry, "CreatedBy", userName);
          SetProperty(type, entityEntry, "CreatedAt", DateTime.UtcNow);
     }
     else if (entityEntry.State == EntityState.Modified)
     {
          SetProperty(type, entityEntry, "UpdatedBy", userName);
          SetProperty(type, entityEntry, "UpdatedAt", DateTime.UtcNow);
     }
}
4 Likes

Now that's what I call support. Thank you so much @korchev!

1 Like

I've got time to try and implement this, this morning and I'm running into an issue in the SaveChanges override.

It's failing on this line : var userName = httpAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;

I can see that it's failing to get the logged in userName. Diving into the identity I can see it looks like the logged in user isn't authenticated(?), I think. I've logged the user out and logged it back in and still can't get it to find the username.

Thanks.

This will happen if the first step is missing - the Startup.Audit.cs file.

Typo alert! :sob:

Thanks @korchev

I have been looking at implementing this today in my Blazor sample application and I seem to be running into a problem because the httpAccessor isn't injected into the DbContext when the project is built. I have implemented security I can see by looking at the DbContext that the httpAccessor doesn't exist.

Would it be an issue to implement this in the Blazor projects too?

Thank you.

The current user in Blazor is obtained by other means: https://www.radzen.com/documentation/blazor/get-current-user/

Ok, thanks for the link.

I've added:

private readonly SecurityService security;

public PaccorDbContext(DbContextOptions<PaccorDbContext> options, SecurityService security) : this(options)
{
    this.security = security;
}

To my MyContext.Audit.cs class file. and in the SaveChanges() method changed:

var userName = httpAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;

to:

var userName = security.User.UserName;

The application builds and starts but I get this error in chrome just as the pages were about to load:

InvalidOperationException: No authentication handler is registered for the scheme 'Bearer'. The registered schemes are: Identity.Application, Identity.External, Identity.TwoFactorRememberMe, Identity.TwoFactorUserId. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("Bearer",...)?

In Blazor you don't need the Startup.Audit.cs file.