Scotec.AspNetCore.DbContextWatcher
1.0.0-dev.15
See the version list below for details.
dotnet add package Scotec.AspNetCore.DbContextWatcher --version 1.0.0-dev.15
NuGet\Install-Package Scotec.AspNetCore.DbContextWatcher -Version 1.0.0-dev.15
<PackageReference Include="Scotec.AspNetCore.DbContextWatcher" Version="1.0.0-dev.15" />
paket add Scotec.AspNetCore.DbContextWatcher --version 1.0.0-dev.15
#r "nuget: Scotec.AspNetCore.DbContextWatcher, 1.0.0-dev.15"
// Install Scotec.AspNetCore.DbContextWatcher as a Cake Addin #addin nuget:?package=Scotec.AspNetCore.DbContextWatcher&version=1.0.0-dev.15&prerelease // Install Scotec.AspNetCore.DbContextWatcher as a Cake Tool #tool nuget:?package=Scotec.AspNetCore.DbContextWatcher&version=1.0.0-dev.15&prerelease
Scotec.AspNetCore.DbContextWatcher
In a REST WebAPI, the best practice is to write data to the database before sending the response to the client. This approach ensures data consistency and integrity. Here’s why:
<b>Data Integrity</b>: Ensures that the changes are committed successfully before informing the client. This avoids scenarios where the client is informed of a successful operation, but the data is not actually saved.
<b>Error Handling</b>: Allows you to handle any errors or exceptions during the database write operation and send an appropriate error response to the client.
<b>Transactional Consistency</b>: Guarantees that the entire operation (including any business logic and database writes) is completed within a single transaction, ensuring atomicity.
DbContextWatcherMiddleware checks the current status of the DbContext before a response is sent back to the client. If the DbContext contains modified data, the response is not allowed to be sent and an error code is returned instead.
Safety and idempotency
<a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods">Save</a> and <a href="http://en.wikipedia.org/wiki/Idempotence">idempotent</a> methods such as GET or HEAD should never change the status of the server. This also includes creating, modifying or deleting data in the database. The DbContextWatcherMiddleware checks all incoming requests and sets the DbContext" into the readonly state. Attempts to call the Save method lead to an exception.
Change tracking
When making read or write API calls, the same code parts are often used internally for querying entities. While change tracking is often switched on for write calls (e.g. POST or PUT), it should be deactivated for purely read accesses (e.g. GET) for performance reasons.
However, this means that tracking must be activated or deactivated at these points depending on the request.
DbContextWatcher automatically sets the default behaviour for the tracking behaviour of the DbContext to TrackingAll
for write API calls and to NoTrackingWithIdentityResolution
for read API calls.
Change detection
When processing read-only API calls, DbContextWatcher checks whether an attempt was made to modify data immediately before returning the response to the client. For this purpose, HasChanges
is called on the change tracker. If any changes are recognised here, DbContextWatcher interprets this as an attempt to manipulate data and returns an error to the client. This can be particularly helpful when developing an app in order to detect potential errors that could lead to data inconsistency or performance issues.
How to use DbContextWatcher
DbContextWatcher is implemented as middleware. In order to activate the desired monitoring of the DbContext as early as possible, DbContextWatcher should be installed as one of the first middlewares if possible, but at the latest before the first middleware that initiates database calls.
To use the standard behaviour of DbContextWatcher, it is sufficient to register the DbContextWatcher middleware.
app.UseMiddleware<DbContextWatcherMiddleware<MyDbContext>>();
DbContextWatcher requires a generic type parameter that specifies the type of your DbContext.
Create your own...
If you want more control over the behaviour of DbContextWatcher, you can derive your own class from it.
public class MyDbContextWatcherMiddleware : DbContextWatcherMiddleware<MyDbContext>
{
public MyDbContextWatcherMiddleware(RequestDelegate next) : base(next)
{
}
protected override Task OnInvokeAsync()
{
return base.OnInvokeAsync();
}
protected override Task<bool> CanSaveChangesAsync()
{
return base.CanSaveChangesAsync();
}
protected override Task<bool> HasChangesAsync()
{
return base.HasChangesAsync();
}
protected override Task SendResponseAsync(DbContextWatcherException exception)
{
return base.SendResponseAsync(exception);
}
}
To change the behaviour of DbContextWatcher, the following methods can be overridden.
OnInvokeAsync
OnInvokeAsync is called immediately after the middleware is invoked. Here you have the option of making settings in the DbContext. DbContextWatcher sets the tracking behaviour of the DbContext to TrackAll
or NoTrackingWithIdentityResolution
depending on the HTTP method (POST, PUT, GET, etc.).
protected virtual async Task OnInvokeAsync()
{
DbContext.ChangeTracker.QueryTrackingBehavior = await CanSaveChangesAsync()
? QueryTrackingBehavior.TrackAll
: QueryTrackingBehavior.NoTrackingWithIdentityResolution;
}
CanSaveChangesAsync
CanSaveChangesAsync determines whether the DbContext may save changes to the database or not.
protected virtual Task<bool> CanSaveChangesAsync()
{
return Task.FromResult(!SaveHttpMethods.Contains(HttpContext.Request.Method));
}
HasChangesAsync
You can implement your own checks by overriding the method. For example, you may want to write log information to the database. In this case, new entries in the log table could be ignored and the DbContext considered unchanged. However, even if this is a valid use case, the use of a separate DbContext for writing to the log table should be considered. HasChangesAsync checks whether the change tracker has detected any modified data.
protected virtual Task<bool> HasChangesAsync()
{
return Task.FromResult(DbContext.ChangeTracker.HasChanges());
}
SendResponseAsync
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Lib.Harmony (>= 2.3.3)
- System.Text.Json (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.3 | 77 | 11/23/2024 |
1.0.2 | 73 | 11/23/2024 |
1.0.0 | 73 | 11/23/2024 |
1.0.0-dev.17 | 38 | 11/23/2024 |
1.0.0-dev.15 | 34 | 11/23/2024 |
1.0.0-dev.7 | 38 | 11/22/2024 |
1.0.0-dev.6 | 34 | 11/21/2024 |