|
1 | | -# Pandatech.*** |
| 1 | +# Pandatech.EFCore.Audit |
2 | 2 |
|
3 | | -## Introduction |
| 3 | +`Pandatech.EFCore.Audit` is a powerful and configurable library designed to collect audit trail data from the EF Core |
| 4 | +`DbContext` change tracker. It is built with scalability and professional-grade extensibility in mind. |
4 | 5 |
|
5 | 6 | ## Features |
6 | 7 |
|
| 8 | +- **Scalable & Configurable**: Tailor the behavior to meet your project's needs. |
| 9 | +- **Composite Key Handling**: Returns concatenated composite keys in a single property using `_` as the delimiter. |
| 10 | +- **Property Transformation**: Customize tracked properties (e.g., rename, transform, or ignore). |
| 11 | + |
| 12 | +## Limitations |
| 13 | + |
| 14 | +- Not atomic: Being event-based, there is a risk of losing audit data in edge cases. |
| 15 | +- Does not work with untracked operations like `AsNoTracking`, `ExecuteUpdate`, `ExecuteDelete`, etc. |
| 16 | + |
7 | 17 | ## Installation |
8 | 18 |
|
9 | | -## Usage |
| 19 | +Install the NuGet package: |
| 20 | + |
| 21 | +```bash |
| 22 | +dotnet add package Pandatech.EFCore.Audit |
| 23 | +``` |
| 24 | + |
| 25 | +## Integration |
| 26 | + |
| 27 | +To integrate `Pandatech.EFCore.Audit` into your project,, follow these steps: |
| 28 | + |
| 29 | +### 1. Configure DbContext |
| 30 | + |
| 31 | +Set up your `DbContext` to include your entities: |
| 32 | + |
| 33 | +```csharp |
| 34 | +public class PostgresContext(DbContextOptions options) : DbContext(options) |
| 35 | +{ |
| 36 | + public DbSet<Blog> Blogs { get; set; } |
| 37 | + public DbSet<Post> Posts { get; set; } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | +### 2. Configure Entities |
| 42 | + |
| 43 | +Entities can be set up for auditing using custom configurations. Below are examples: |
| 44 | + |
| 45 | +#### Blog Entity |
| 46 | + |
| 47 | +```csharp |
| 48 | +public class Blog |
| 49 | +{ |
| 50 | + public int Id { get; set; } |
| 51 | + public required string Title { get; set; } |
| 52 | + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; |
| 53 | + public BlogType BlogType { get; set; } |
| 54 | + public required byte[] EncryptedKey { get; set; } |
| 55 | +} |
| 56 | + |
| 57 | +public class BlogAuditTrailConfiguration : AuditTrailConfigurator<Blog> |
| 58 | +{ |
| 59 | + public BlogAuditTrailConfiguration() |
| 60 | + { |
| 61 | + SetReadPermission(Permission.UserPermission); |
| 62 | + WriteAuditTrailOnEvents(AuditActionType.Create, AuditActionType.Update, AuditActionType.Delete); |
| 63 | + RuleFor(s => s.EncryptedKey).Transform(Convert.ToBase64String); |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +public enum Permission |
| 68 | +{ |
| 69 | + AdminPermission, |
| 70 | + UserPermission |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +#### Post Entity |
| 75 | + |
| 76 | +```csharp |
| 77 | +public class Post |
| 78 | +{ |
| 79 | + public int Id { get; set; } |
| 80 | + public int BlogId { get; set; } |
| 81 | + public required string Title { get; set; } |
| 82 | + public required string Content { get; set; } |
| 83 | + public Blog Blog { get; set; } = null!; |
| 84 | +} |
| 85 | + |
| 86 | +public class PostAuditConfiguration : AuditTrailConfigurator<Post> |
| 87 | +{ |
| 88 | + public PostAuditConfiguration() |
| 89 | + { |
| 90 | + SetServiceName("BlogService"); |
| 91 | + RuleFor(s => s.Content).Ignore(); |
| 92 | + RuleFor(s => s.Title).Rename("TotallyNewTitle"); |
| 93 | + } |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +#### Configuration Details |
| 98 | + |
| 99 | +- **SetServiceName:** Specifies a custom service name that will be returned during the audit trail event. This can be |
| 100 | + useful for identifying the origin of the change. |
| 101 | +- **SetReadPermission:** Assigns a predefined permission level included in the event, enabling better control over who |
| 102 | + can access the audit information as row-level security. |
| 103 | +- **WriteAuditTrailOnEvents:** Defines the specific events (`Create`, `Update`, `Delete`) on which an entity should be |
| 104 | + tracked. If this option is not configured, all events for the entity **will be tracked by default**. |
| 105 | +- **Exclusion of Configuration:** If an entity should not be audited, its configuration should be omitted entirely. |
| 106 | + Entities |
| 107 | + without configuration will not be tracked. |
| 108 | +- **Transform:** Allows you to apply a custom function to modify the value of a property before it is recorded in the |
| 109 | + audit trail. For example, this can be used to encrypt/decrypt or format data. |
| 110 | +- **Ignore:** Skips tracking of the specified property within the entity. Useful for sensitive or irrelevant data. |
| 111 | +- **Rename:** Changes the property name in the audit trail output. This is useful for aligning property names with |
| 112 | + business-specific terminology or conventions. |
| 113 | + |
| 114 | +### 3. Register `DbContext` in `Program.cs` |
| 115 | + |
| 116 | +Register your `DbContext` and add the audit trail interceptors: |
| 117 | + |
| 118 | +```csharp |
| 119 | +public static WebApplicationBuilder AddPostgresContext<TContext>(this WebApplicationBuilder builder, |
| 120 | + string connectionString) |
| 121 | + where TContext : DbContext |
| 122 | +{ |
| 123 | + builder.Services.AddDbContextPool<TContext>((sp, options) => |
| 124 | + { |
| 125 | + options |
| 126 | + .UseNpgsql(connectionString) |
| 127 | + .AddAuditTrailInterceptors(sp); |
| 128 | + }); |
| 129 | + |
| 130 | + return builder; |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +### 4. Set Up the Audit Trail Subscriber |
| 135 | + |
| 136 | +Configure how you want to handle audit trail events: |
| 137 | + |
| 138 | +```csharp |
| 139 | +AuditTrailSubscriber.ConfigureAuditTrailHandler(auditTrailEventData => |
| 140 | +{ |
| 141 | + // Implement your custom logic here |
| 142 | + Console.WriteLine("Audit Trail Event Received:"); |
| 143 | + Console.WriteLine(JsonSerializer.Serialize(auditTrailEventData)); |
| 144 | +}); |
| 145 | +``` |
| 146 | + |
| 147 | +### 5. Example `Program.cs` |
| 148 | + |
| 149 | +Below is a simplified example of how your Program.cs might look: |
| 150 | + |
| 151 | +```csharp |
| 152 | +var builder = WebApplication.CreateBuilder(args); |
| 153 | + |
| 154 | +// Register audit trail configurations |
| 155 | +builder.Services.AddAuditTrail(typeof(Program).Assembly); |
| 156 | + |
| 157 | +// Register DbContext with audit trail interceptors |
| 158 | +builder.AddPostgresContext<PostgresContext>( |
| 159 | + "Server=localhost;Port=5432;Database=audit_test;User Id=test;Password=test;Pooling=true;"); |
| 160 | + |
| 161 | +var app = builder.Build(); |
| 162 | + |
| 163 | +// Configure audit trail event handling |
| 164 | +AuditTrailSubscriber.ConfigureAuditTrailHandler(auditTrailEventData => |
| 165 | +{ |
| 166 | + // Implement your custom logic here |
| 167 | + Console.WriteLine("Audit Trail Event Received:"); |
| 168 | + Console.WriteLine(JsonSerializer.Serialize(auditTrailEventData)); |
| 169 | +}); |
| 170 | + |
| 171 | +app.Run(); |
| 172 | +``` |
| 173 | + |
| 174 | +### 6. Audit Trail Event Data |
| 175 | + |
| 176 | +The audit trail event data is represented by the following classes: |
| 177 | + |
| 178 | +```csharp |
| 179 | +public record AuditTrailEventData(List<AuditTrailEventEntity> Entities); |
| 180 | + |
| 181 | +public record AuditTrailEventEntity( |
| 182 | + string? ServiceName, |
| 183 | + AuditActionType ActionType, |
| 184 | + string EntityName, |
| 185 | + object? ReadPermission, |
| 186 | + string PrimaryKeyValue, |
| 187 | + Dictionary<string, object?> TrackedProperties); |
| 188 | +``` |
| 189 | + |
| 190 | +- AuditTrailEventData: Contains a list of `AuditTrailEventEntity` objects. |
| 191 | +- AuditTrailEventEntity: Represents an audited entity with its associated data. |
| 192 | + - ServiceName: The name of the service where the change originated. Configured manually using `SetServiceName`. |
| 193 | + - ActionType: The type of action performed (`Create`, `Update`, `Delete`). |
| 194 | + - EntityName: The name of the entity. |
| 195 | + - ReadPermission: The assigned permission level for accessing this audit trail. Configured manually using |
| 196 | + `SetReadPermission`. |
| 197 | + - PrimaryKeyValue: The primary key value(s) of the entity. |
| 198 | + - TrackedProperties: A dictionary containing the tracked properties and their values. |
| 199 | + |
| 200 | +## Notes |
| 201 | + |
| 202 | +- **Partial Property Tracking:** For `Update` actions, `TrackedProperties` only includes properties that have been |
| 203 | + modified. |
| 204 | +- **Event Handling:** The provided `Console.WriteLine` in the demo is a placeholder. You are responsible for |
| 205 | + implementing your own event handling logic. |
| 206 | +- **Database Compatibility:** Compatible with PostgreSQL and other relational databases supported by EF Core. |
| 207 | +- **Compatible with `.Net 9 +` |
10 | 208 |
|
11 | 209 | ## License |
12 | 210 |
|
13 | | -Pandatech.*** is licensed under the MIT License. |
| 211 | +This project is licensed under the MIT License. |
0 commit comments