{"id":22106631,"url":"https://github.com/SimonCropp/Delta","last_synced_at":"2025-07-25T03:32:01.951Z","repository":{"id":64278727,"uuid":"572273596","full_name":"SimonCropp/Delta","owner":"SimonCropp","description":"An approach to implementing a 304 Not Modified leveraging DB change tracking","archived":false,"fork":false,"pushed_at":"2025-07-21T12:19:49.000Z","size":754,"stargazers_count":1409,"open_issues_count":1,"forks_count":35,"subscribers_count":16,"default_branch":"main","last_synced_at":"2025-07-21T14:25:25.737Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/SimonCropp.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"license.txt","code_of_conduct":"code_of_conduct.md","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,"zenodo":null},"funding":{"github":"SimonCropp"}},"created_at":"2022-11-29T23:10:40.000Z","updated_at":"2025-07-21T12:19:53.000Z","dependencies_parsed_at":"2024-11-06T16:38:28.892Z","dependency_job_id":"eee3ed12-4632-4106-85a8-3a27fcb9bf43","html_url":"https://github.com/SimonCropp/Delta","commit_stats":{"total_commits":365,"total_committers":4,"mean_commits":91.25,"dds":0.452054794520548,"last_synced_commit":"97af367eb4abab5772630ea61bffbd4aa1012f59"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/SimonCropp/Delta","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FDelta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FDelta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FDelta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FDelta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimonCropp","download_url":"https://codeload.github.com/SimonCropp/Delta/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FDelta/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266321322,"owners_count":23910805,"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","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-01T08:01:43.623Z","updated_at":"2025-07-25T03:32:01.926Z","avatar_url":"https://github.com/SimonCropp.png","language":"C#","readme":"\u003c!--\nGENERATED FILE - DO NOT EDIT\nThis file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets).\nSource File: /readme.source.md\nTo change this file edit the source file and then run MarkdownSnippets.\n--\u003e\n\n# \u003cimg src=\"/src/icon.png\" height=\"30px\"\u003e Delta\n\n[![Build status](https://ci.appveyor.com/api/projects/status/20t96gnsmysklh09/branch/main?svg=true)](https://ci.appveyor.com/project/SimonCropp/Delta)\n[![NuGet Status](https://img.shields.io/nuget/v/Delta.svg?label=Delta)](https://www.nuget.org/packages/Delta/)\n[![NuGet Status](https://img.shields.io/nuget/v/Delta.EF.svg?label=Delta.EF)](https://www.nuget.org/packages/Delta.EF/)\n[![NuGet Status](https://img.shields.io/nuget/v/Delta.SqlServer.svg?label=Delta.SqlServer)](https://www.nuget.org/packages/Delta.SqlServer/)\n\nDelta is an approach to implementing a [304 Not Modified](https://www.keycdn.com/support/304-not-modified) leveraging DB change tracking.\u003c!-- include: intro. path: /docs/mdsource/intro.include.md --\u003e\n\nThe approach uses a last updated timestamp from the database to generate an [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag). All dynamic requests then have that ETag checked/applied.\n\nThis approach works well when the frequency of updates is relatively low. In this scenario, the majority of requests will leverage the result in a 304 Not Modified being returned and the browser loading the content its cache.\n\nEffectively consumers will always receive the most current data, while the load on the server is reduced.\u003c!-- endInclude --\u003e\n\n**See [Milestones](../../milestones?state=closed) for release notes.**\n\n\n## Sponsors\n\n### Entity Framework Extensions\u003c!-- include: zzz. path: /docs/mdsource/zzz.include.md --\u003e\n\n[Entity Framework Extensions](https://entityframework-extensions.net/?utm_source=simoncropp\u0026utm_medium=Delta) is a major sponsor and is proud to contribute to the development this project.\n\n[![Entity Framework Extensions](https://raw.githubusercontent.com/SimonCropp/Delta/refs/heads/main/docs/zzz.png)](https://entityframework-extensions.net/?utm_source=simoncropp\u0026utm_medium=Delta)\u003c!-- endInclude --\u003e\n\n\n### JetBrains\n\n[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport)\n\n\n## Jump to specific docs\n\n * [SQL Server Docs](/docs/sqlserver.md) when using [SQL Server SqlClient](https://github.com/dotnet/SqlClient)\n * [PostgreSQL Docs](/docs/postgres.md) when using [PostgreSQL Npgsql](https://www.npgsql.org)\n * [EF with SQL Server Docs](/docs/sqlserver-ef.md) when using the [SQL Server EF Database Provider](https://learn.microsoft.com/en-us/ef/core/providers/sql-server/?tabs=dotnet-core-cli)\n * [EF with PostgreSQL Docs](/docs/postgres-ef.md) when using the [PostgreSQL EF Database Provider](https://www.npgsql.org/efcore)\n\n\n## Assumptions\n\nFrequency of updates to data is relatively low compared to reads\n\n\n## 304 Not Modified Flow\n\n```mermaid\ngraph TD\n    Request\n    CalculateEtag[Calculate current ETag\u003cbr/\u003ebased on timestamp\u003cbr/\u003efrom web assembly and SQL]\n    IfNoneMatch{Has\u003cbr/\u003eIf-None-Match\u003cbr/\u003eheader?}\n    EtagMatch{Current\u003cbr/\u003eEtag matches\u003cbr/\u003eIf-None-Match?}\n    AddETag[Add current ETag\u003cbr/\u003eto Response headers]\n    304[Respond with\u003cbr/\u003e304 Not-Modified]\n    Request --\u003e CalculateEtag\n    CalculateEtag --\u003e IfNoneMatch\n    IfNoneMatch --\u003e|Yes| EtagMatch\n    IfNoneMatch --\u003e|No| AddETag\n    EtagMatch --\u003e|No| AddETag\n    EtagMatch --\u003e|Yes| 304\n```\n\n## DB implementation\n\nImplementation is specific to the target database\n\n * [SQL Server implementation](/docs/sqlserver.md#implementation)\n * [PostgreSQL implementation](/docs/postgres.md#implementation)\n\n\n## ETag calculation logic\n\nThe ETag is calculated from a combination several parts\n\n```\n{AssemblyWriteTime}-{DbTimeStamp}-{Suffix}\n```\n\n\n### AssemblyWriteTime\n\nThe last write time of the web entry point assembly\n\n\u003c!-- snippet: AssemblyWriteTime --\u003e\n\u003ca id='snippet-AssemblyWriteTime'\u003e\u003c/a\u003e\n```cs\nvar webAssemblyLocation = Assembly.GetEntryAssembly()!.Location;\nAssemblyWriteTime = File.GetLastWriteTime(webAssemblyLocation).Ticks.ToString();\n```\n\u003csup\u003e\u003ca href='/src/Delta/DeltaExtensions_Shared.cs#L44-L49' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-AssemblyWriteTime' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n### DB timestamp\n\nTimestamp calculation is specific to the target database\n\n * [SQL Server timestamp calculation](/docs/sqlserver.md#timestamp-calculation)\n * [Postgres timestamp calculation](/docs/postgres.md#timestamp-calculation)\n\n\n### Suffix\n\nAn optional string suffix that is dynamically calculated at runtime based on the current `HttpContext`.\n\n\u003c!-- snippet: Suffix --\u003e\n\u003ca id='snippet-Suffix'\u003e\u003c/a\u003e\n```cs\nvar app = builder.Build();\napp.UseDelta(suffix: httpContext =\u003e \"MySuffix\");\n```\n\u003csup\u003e\u003ca href='/src/DeltaTests/Usage.cs#L9-L14' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Suffix' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n### Combining the above\n\n\u003c!-- snippet: BuildEtag --\u003e\n\u003ca id='snippet-BuildEtag'\u003e\u003c/a\u003e\n```cs\ninternal static string BuildEtag(string timeStamp, string? suffix)\n{\n    if (suffix == null)\n    {\n        return $\"\\\"{AssemblyWriteTime}-{timeStamp}\\\"\";\n    }\n\n    return $\"\\\"{AssemblyWriteTime}-{timeStamp}-{suffix}\\\"\";\n}\n```\n\u003csup\u003e\u003ca href='/src/Delta/DeltaExtensions_Shared.cs#L152-L164' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-BuildEtag' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n## Usage\n\nDelta has two approaches to usage:\n\n\n### Raw DbConnection approach\n\nDelivers functionality using DbConnection and DbTransaction.\n\nNuGet: [Delta](https://nuget.org/packages/Delta/)\n\nDocumentation is specific to choice of database:\n\n * [SQL Server Docs](/docs/sqlserver.md) when using [SQL Server SqlClient](https://github.com/dotnet/SqlClient)\n * [PostgreSQL Docs](/docs/postgres.md) when using [PostgreSQL Npgsql](https://www.npgsql.org)\n\n\n### Entity Framework approach\n\nDelivers functionality using [Entity Framework](https://learn.microsoft.com/en-us/ef/).\n\nNuGet: [Delta.EF](https://nuget.org/packages/Delta.EF/)\n\nDocumentation is specific to choice of database:\n\n * [EF with SQL Server Docs](/docs/sqlserver-ef.md) when using the [SQL Server EF Database Provider](https://learn.microsoft.com/en-us/ef/core/providers/sql-server/?tabs=dotnet-core-cli)\n * [EF with PostgreSQL Docs](/docs/postgres-ef.md) when using the [PostgreSQL EF Database Provider](https://www.npgsql.org/efcore)\n\n\n## UseResponseDiagnostics\n\nResponse diagnostics is an opt-out feature that includes extra log information in the response headers.\n\nDisable by setting UseResponseDiagnostics to false at startup:\n\n\u003c!-- snippet: UseResponseDiagnostics --\u003e\n\u003ca id='snippet-UseResponseDiagnostics'\u003e\u003c/a\u003e\n```cs\nDeltaExtensions.UseResponseDiagnostics = false;\n```\n\u003csup\u003e\u003ca href='/src/DeltaTests/UseResponseDiagnosticsSnippet.cs#L4-L6' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-UseResponseDiagnostics' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nResponse diagnostics headers are prefixed with `Delta-`.\n\nExample Response header when the Request has not `If-None-Match` header.\n\n\u003cimg src=\"/src/Delta-No304.png\"\u003e\n\n\n## Verifying behavior\n\nThe behavior of Delta can be verified as follows:\n\n * Open a page in the site\n * Open the browser developer tools\n * Change to the Network tab\n * Refresh the page.\n\nCached responses will show as 304 in the `Status`:\n\n\u003cimg src=\"/src/network.png\"\u003e\n\nIn the headers `if-none-match` will show in the request and `etag` will show in the response:\n\n\u003cimg src=\"/src/network-details.png\"\u003e\n\n\n### Ensure cache is not disabled\n\nIf disable cache is checked, the browser will not send the `if-none-match` header. This will effectively cause a cache miss server side, and the full server pipeline will execute.\n\n\u003cimg src=\"/src/disable-cache.png\"\u003e\n\n\n### Certificates and Chromium\n\nChromium, and hence the Chrome and Edge browsers, are very sensitive to certificate problems when determining if an item should be cached. Specifically, if a request is done dynamically (type: xhr) and the server is using a self-signed certificate, then the browser will not send the `if-none-match` header. [Reference]( https://issues.chromium.org/issues/40666473). If self-signed certificates are required during development in lower environment, then use FireFox to test the caching behavior. \n\n\n## Programmatic client usage\n\nDelta is primarily designed to support web browsers as a client. All web browsers have the necessary 304 and caching functionally required.\n\nIn the scenario where web apis (that support using 304) are being consumed using .net as a client, consider using one of the below extensions to cache responses.\n\n * [Replicant](https://github.com/SimonCropp/Replicant)\n * [Tavis.HttpCache](https://github.com/tavis-software/Tavis.HttpCache)\n * [CacheCow](https://github.com/aliostad/CacheCow)\n * [Monkey Cache](https://github.com/jamesmontemagno/monkey-cache)\n\n\n## Icon\n\n[Estuary](https://thenounproject.com/term/estuary/1847616/) designed by [Daan](https://thenounproject.com/Asphaleia/) from [The Noun Project](https://thenounproject.com).\n","funding_links":["https://github.com/sponsors/SimonCropp"],"categories":["Others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSimonCropp%2FDelta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSimonCropp%2FDelta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSimonCropp%2FDelta/lists"}