Improve Data Flow through React Context
28.02.2024
17.07.2023
Of course, properties like ‘CreatedBy’ or ‘DateModified’ don’t replace a clean full historisation, but often we just need a small piece of information about who created this entry or when it was edited last. To get this information without having to join other tables (most of the queries have too many joins already anyway), we went with the habit of adding the following properties to all of our entities:
These are only some possible sample names to understand the information that is saved behind these properties; of course, we respect the opinion of every developer and therefore leave the decision to name them up to them, hoping that they will be as different as possible in every project, preferably in every entity. No, of course not. That is also why one of the first things we are going to do is to create a BaseEntity (comments, usings, etc. are removed to keep it short and neat here):
public class BaseEntity
{
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public DateTime? DateModified { get; set; }
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
}
The only other thing we need to pay attention to now is to inherit every entity from this BaseEntity:
public class User : BaseEntity
{
...
}
And we already have the same naming everywhere without much effort. To continue this pattern of having everything ‘the same’, wouldn’t it be nice to also have these properties filled automatically and, more importantly, in always the same way? Because else we can be sure they will be sometimes filled, sometimes only some of them will be filled, some will have the username, some the email address and even the dates will be a mix of UTC and server time. So let’s unify them by automatically handling them with the entity framework.
To achieve this, we need to add a function to our database context class (that inherits from DbContext) which updates these properties based on the EntityEntryEventArgs object it receives:
private void UpdateBaseEntity(object sender, EntityEntryEventArgs e)
{
if (e.Entry.Entity is BaseEntity baseEntity)
{
if (e.Entry.State == EntityState.Added)
{
// Only set values if not set manually
baseEntity.CreatedBy ??= this.contextInformationService.Email;
baseEntity.DateModified ??= DateTime.UtcNow;
baseEntity.ModifiedBy ??= this.contextInformationService.Email;
}
else if (e.Entry.State == EntityState.Modified)
{
// Only change date modified if not manually set
if (baseEntity.DateModified == e.Entry.OriginalValues
.GetValue<DateTime?>(nameof(BaseEntity.DateModified)))
{
baseEntity.DateModified = DateTime.UtcNow;
}
// Only change modified by if not manually set
if (baseEntity.ModifiedBy == e.Entry.OriginalValues
.GetValue<string>(nameof(BaseEntity.ModifiedBy)))
{
baseEntity.ModifiedBy = this.contextInformationService.Email;
}
}
else
{
// Do nothing
}
}
}
In this function, we check first that the changed entry is inherited from the BaseEntity, otherwise we sadly ignore it (to be more strict, it could also throw an exception to prevent having entities that aren’t inherited from the BaseEntity). If we are sure it has the correct properties, we differ between the two actions we want to catch, Added and Modified. In our case, we want the properties to be handled automatically but still provide the possibility for some edge cases to set them manually. Because of this, we add some additional checks: if this option is not needed, the properties can also be strictly set every time. This is also the reason why, in this example, the DateCreated is set directly inside the BaseEntity and isn’t touched in the update function anymore.
Everything should be ready now. The only thing left is to register this update function so it gets called for every change that we want to track. For this, we have to register the event in the constructor of the database context with the ChangeTracker:
public MyOwnContext(DbContextOptions options)
: base(options)
{
this.ChangeTracker.StateChanged += this.UpdateBaseEntity;
this.ChangeTracker.Tracked += this.UpdateBaseEntity;
}
Now it is all set up and no one has to take care of these properties anymore (unless they explicitly want to)!
Improve Data Flow through React Context
28.02.2024
Play with the WBS
13.09.2023
Zentralisierung von Application Logs
12.05.2023
A gentle introduction to JSON Web Tokens
30.03.2023