{"id":22591286,"url":"https://github.com/pandatecham/be-lib-efcore-audit","last_synced_at":"2025-03-28T18:24:34.383Z","repository":{"id":266259443,"uuid":"897156726","full_name":"PandaTechAM/be-lib-efcore-audit","owner":"PandaTechAM","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-12T13:52:52.000Z","size":195,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"development","last_synced_at":"2025-03-12T14:39:41.957Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PandaTechAM.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-02T06:10:19.000Z","updated_at":"2025-03-12T13:47:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"0be35aaa-c11d-4dd2-809f-56b587019f1a","html_url":"https://github.com/PandaTechAM/be-lib-efcore-audit","commit_stats":null,"previous_names":["pandatecham/be-lib-efcore-audit"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PandaTechAM%2Fbe-lib-efcore-audit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PandaTechAM%2Fbe-lib-efcore-audit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PandaTechAM%2Fbe-lib-efcore-audit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PandaTechAM%2Fbe-lib-efcore-audit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PandaTechAM","download_url":"https://codeload.github.com/PandaTechAM/be-lib-efcore-audit/tar.gz/refs/heads/development","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246077981,"owners_count":20720086,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-08T09:12:04.061Z","updated_at":"2025-03-28T18:24:34.361Z","avatar_url":"https://github.com/PandaTechAM.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pandatech.EFCore.Audit\n\n`Pandatech.EFCore.Audit` is a powerful and configurable library designed to collect audit trail data from the EF Core\n`DbContext` change tracker. It is built with scalability and professional-grade extensibility in mind.\n\n## Features\n\n- **Scalable \u0026 Configurable**: Tailor the behavior to meet your project's needs.\n- **Composite Key Handling**: Returns concatenated composite keys in a single property using `_` as the delimiter.\n- **Property Transformation**: Customize tracked properties (e.g., rename, transform, or ignore).\n\n## Limitations\n\n- Not atomic: Being event-based, there is a risk of losing audit data in edge cases.\n- Does not work with untracked operations like `AsNoTracking`, `ExecuteUpdate`, `ExecuteDelete`, etc. For\n  such scenarios, use the new manual bulk audit feature described below.\n\n## Installation\n\nInstall the NuGet package:\n\n```bash\ndotnet add package Pandatech.EFCore.Audit\n```\n\n## Integration\n\nTo integrate `Pandatech.EFCore.Audit` into your project, follow these steps:\n\n### 1. Configure DbContext\n\nSet up your `DbContext` to include your entities:\n\n```csharp\npublic class PostgresContext(DbContextOptions options) : DbContext(options)\n{\n    public DbSet\u003cBlog\u003e Blogs { get; set; }\n    public DbSet\u003cPost\u003e Posts { get; set; }\n}\n```\n\n### 2. Configure Entities\n\nEntities can be set up for auditing using custom configurations. Below are examples:\n\n#### Blog Entity\n\n```csharp\npublic class Blog\n{\n    public int Id { get; set; }\n    public required string Title { get; set; }\n    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;\n    public BlogType BlogType { get; set; }\n    public required byte[] EncryptedKey { get; set; }\n}\n\npublic class BlogAuditTrailConfiguration : AuditTrailConfigurator\u003cBlog\u003e\n{\n    public BlogAuditTrailConfiguration()\n    {\n        SetReadPermission(Permission.UserPermission);\n        WriteAuditTrailOnEvents(AuditActionType.Create, AuditActionType.Update, AuditActionType.Delete);\n        RuleFor(s =\u003e s.EncryptedKey).Transform(Convert.ToBase64String);\n    }\n}\n\npublic enum Permission\n{\n    AdminPermission,\n    UserPermission\n}\n```\n\n#### Post Entity\n\n```csharp\npublic class Post\n{\n    public int Id { get; set; }\n    public int BlogId { get; set; }\n    public required string Title { get; set; }\n    public required string Content { get; set; }\n    public Blog Blog { get; set; } = null!;\n}\n\npublic class PostAuditConfiguration : AuditTrailConfigurator\u003cPost\u003e\n{\n    public PostAuditConfiguration()\n    {\n        SetServiceName(\"BlogService\");\n        RuleFor(s =\u003e s.Content).Ignore();\n        RuleFor(s =\u003e s.Title).Rename(\"TotallyNewTitle\");\n    }\n}\n```\n\n#### Configuration Details\n\n- **SetServiceName:** Specifies a custom service name that will be returned during the audit trail event. This can be\n  useful for identifying the origin of the change.\n- **SetReadPermission:** Assigns a predefined permission level included in the event, enabling better control over who\n  can access the audit information as row-level security.\n- **WriteAuditTrailOnEvents:** Defines the specific events (`Create`, `Update`, `Delete`) on which an entity should be\n  tracked. If this option is not configured, all events for the entity **will be tracked by default**.\n- **Exclusion of Configuration:** If an entity should not be audited, its configuration should be omitted entirely.\n  Entities\n  without configuration will not be tracked.\n- **Transform:** Allows you to apply a custom function to modify the value of a property before it is recorded in the\n  audit trail. For example, this can be used to encrypt/decrypt or format data.\n- **Ignore:** Skips tracking of the specified property within the entity. Useful for sensitive or irrelevant data.\n- **Rename:** Changes the property name in the audit trail output. This is useful for aligning property names with\n  business-specific terminology or conventions.\n\n### 3. Register `DbContext` in `Program.cs`\n\nRegister your `DbContext` and add the audit trail interceptors:\n\n```csharp\npublic static WebApplicationBuilder AddPostgresContext\u003cTContext\u003e(this WebApplicationBuilder builder,\n    string connectionString)\n    where TContext : DbContext\n{\n    builder.Services.AddDbContextPool\u003cTContext\u003e((sp, options) =\u003e\n    {\n        options\n            .UseNpgsql(connectionString)\n            .AddAuditTrailInterceptors(sp);\n    });\n\n    return builder;\n}\n```\n\n### 4. Set Up the Audit Trail Consumer\n\nTo handle audit trail events, create a consumer class that inherits from `IAuditTrailConsumer` and implements the\n`ConsumeAuditTrailAsync` method. Implement this method to process audit trail events according to your application's\nrequirements — for example, logging the events, sending them to an external service, or storing them in a database.\n\nHere is an example implementation that serializes the event data to JSON and writes it to the console:\n\n```csharp\npublic class AuditTrailConsumer : IAuditTrailConsumer\n{\n   public Task ConsumeAuditTrailAsync(AuditTrailEventData auditTrailEventData)\n   {\n      Console.WriteLine(JsonSerializer.Serialize(auditTrailEventData));\n      return Task.CompletedTask;\n   }\n}\n```\n\n### 5. `Program.cs` Registration and Configuration\n\nBelow is a simplified example of how your `Program.cs` file might look:\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\n// Register audit trail configurations with the consumer class before adding the DbContext\nbuilder.Services.AddAuditTrail\u003cAuditTrailConsumer\u003e(typeof(Program).Assembly);\n\n// Register DbContext with audit trail interceptors\nbuilder.AddPostgresContext\u003cPostgresContext\u003e(\n    \"Server=localhost;Port=5432;Database=audit_test;User Id=test;Password=test;Pooling=true;\");\n\nvar app = builder.Build();\n\napp.Run();\n```\n\n\u003e **Note:** The `AddAuditTrail` method registers the audit trail configurations and `HttpContextAccessor` so it should\n\u003e be always **before** the `AddDbContext` method. In case of using `AddDbContext` before registration make sure to\n\u003e register the `HttpContextAccessor` manually by using `builder.Services.AddHttpContextAccessor()` method in\n`Program.cs`.\n\n### 6. Audit Trail Event Data\n\nThe audit trail event data is represented by the following classes:\n\n```csharp\npublic record AuditTrailEventData(List\u003cAuditTrailEventEntity\u003e Entities);\n\npublic record AuditTrailEventEntity(\n    EntityEntry Entry,\n    string? ServiceName,\n    AuditActionType ActionType,\n    string EntityName,\n    object? ReadPermission,\n    string PrimaryKeyValue,\n    Dictionary\u003cstring, object?\u003e TrackedProperties);\n```\n\n- AuditTrailEventData: Contains a list of `AuditTrailEventEntity` objects.\n- AuditTrailEventEntity: Represents an audited entity with its associated data.\n    - Entry: The `EntityEntry` object containing the entity data from `DbContext`.\n    - ServiceName: The name of the service where the change originated. Configured manually using `SetServiceName`.\n    - ActionType: The type of action performed (`Create`, `Update`, `Delete`).\n    - EntityName: The name of the entity.\n    - ReadPermission: The assigned permission level for accessing this audit trail. Configured manually using\n      `SetReadPermission`.\n    - PrimaryKeyValue: The primary key value(s) of the entity.\n    - TrackedProperties: A dictionary containing the tracked properties and their values.\n\n### 7. Manual Bulk Audit\n\nIf you perform operations outside EF Core’s change tracker (e.g. `AsNoTracking`, `ExecuteUpdate()`, raw SQL queries,\nexternal APIs, etc.), you can still create audit events by manually specifying:\n\n- `AuditActionType` (Create, Update, Delete)\n- Primary key value(s)\n- The dictionary of changed properties relevant to the operation\n\nThis is exposed via a single `IAuditTrailPublisher.BulkAuditAsync` method (or similar) that accepts a list of manual\naudit entries. Each entry contains:\n\n```csharp\npublic record ManualAuditEntry(\n    Type EntityType,              // The CLR type you're auditing (e.g. Blog, Post)\n    AuditActionType Action,       // Create, Update, or Delete\n    List\u003cAuditEntryDetail\u003e ChangedItems // One or more records to track\n);\n\npublic record AuditEntryDetail(\n    List\u003cstring\u003e PrimaryKeyIds,          // e.g. [\"10\"] or [\"10\",\"20\"] for composite PK\n    Dictionary\u003cstring, object?\u003e ChangedProperties // developer-supplied property data\n);\n```\n\n#### Usage Example\n\n```csharp\npublic async Task CreatePublish()\n{\n  var posts = new List\u003cPost\u003e();\n\n  var blog = new Blog\n  {\n     Id = 2,\n     Title = \"null\",\n     CreatedAt = DateTime.UtcNow,\n     BlogType = BlogType.Personal,\n     EncryptedKey = [1, 2, 3]\n  };\n\n  for (var i = 0; i \u003c 10; i++)\n  {\n     var post = new Post\n     {\n        Title = $\"Post {i}\",\n        Content = $\"This is post {i}\",\n        Blog = blog\n     };\n\n     posts.Add(post);\n  }\n\n  var auditEntries = new List\u003cManualAuditEntry\u003e\n  {\n     new(\n        typeof(Blog),\n        AuditActionType.Create,\n        [\n           new AuditEntryDetail(\n              PrimaryKeyIds: [blog.Id.ToString()],\n\n              ChangedProperties: new Dictionary\u003cstring, object?\u003e\n              {\n                 [nameof(blog.Id)] = blog.Id,\n                 [nameof(blog.Title)] = blog.Title,\n                 [nameof(blog.CreatedAt)] = blog.CreatedAt,\n                 [nameof(blog.BlogType)] = blog.BlogType,\n                 [nameof(blog.EncryptedKey)] = blog.EncryptedKey\n              }\n           )\n        ]\n     )\n  };\n\n  var postDetails = new List\u003cAuditEntryDetail\u003e();\n  foreach (var p in posts)\n  {\n     postDetails.Add(\n        new AuditEntryDetail(\n           PrimaryKeyIds: [p.Id.ToString()],\n           ChangedProperties: new Dictionary\u003cstring, object?\u003e\n           {\n              [nameof(p.Title)] = p.Title,\n              [nameof(p.Content)] = p.Content,\n              [nameof(p.BlogId)] = p.BlogId\n           }\n        )\n     );\n  }\n\n  auditEntries.Add(\n     new ManualAuditEntry(\n        EntityType: typeof(Post),\n        Action: AuditActionType.Create,\n        ChangedItems: postDetails\n     )\n  );\n\n  await auditPublisher.BulkAuditAsync(auditEntries); // IAuditTrailPublisher.BulkAuditAsync\n}\n```\n\nWhen you call `BulkAuditAsync`, the library will apply any configured transformations (ignore, rename, transform) and\npublish the resulting audit trail event.\n\n## Notes\n\n- **Partial Property Tracking:** For `Update` actions, `TrackedProperties` only includes properties that have been\n  modified.\n- **Event Handling:** The provided `Console.WriteLine` in the demo is a placeholder. You are responsible for\n  implementing your own event handling logic.\n- **Database Compatibility:** Compatible with PostgreSQL and other relational databases supported by EF Core.\n- **Compatible with `.Net 9 +`\n\n## License\n\nThis project is licensed under the MIT License.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpandatecham%2Fbe-lib-efcore-audit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpandatecham%2Fbe-lib-efcore-audit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpandatecham%2Fbe-lib-efcore-audit/lists"}