{"id":22343835,"url":"https://github.com/orangeloop/sagas","last_synced_at":"2025-07-30T03:30:36.136Z","repository":{"id":49813652,"uuid":"181565416","full_name":"orangeloop/sagas","owner":"orangeloop","description":".NET implementation of the UnitOfWork pattern using IDbTransaction and Sagas for orchestrating cross-domain transactions.","archived":false,"fork":false,"pushed_at":"2024-09-14T23:29:16.000Z","size":59,"stargazers_count":34,"open_issues_count":0,"forks_count":6,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-11T05:16:32.280Z","etag":null,"topics":["asp-net","aspnet-core","csharp","dotnet","dotnet-core","microservices","saga-pattern","sagas","unitofwork","unitofwork-pattern"],"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/orangeloop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-15T21:02:45.000Z","updated_at":"2025-01-09T13:56:54.000Z","dependencies_parsed_at":"2022-08-28T14:00:32.376Z","dependency_job_id":null,"html_url":"https://github.com/orangeloop/sagas","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/orangeloop/sagas","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orangeloop%2Fsagas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orangeloop%2Fsagas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orangeloop%2Fsagas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orangeloop%2Fsagas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/orangeloop","download_url":"https://codeload.github.com/orangeloop/sagas/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orangeloop%2Fsagas/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267803962,"owners_count":24146526,"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-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["asp-net","aspnet-core","csharp","dotnet","dotnet-core","microservices","saga-pattern","sagas","unitofwork","unitofwork-pattern"],"created_at":"2024-12-04T09:06:23.059Z","updated_at":"2025-07-30T03:30:35.819Z","avatar_url":"https://github.com/orangeloop.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OrangeLoop.Sagas\n\n[![Build Status](https://dev.azure.com/livefree/engineering/_apis/build/status%2FOrangeloop%2FOrangeloop.Sagas?branchName=develop)](https://dev.azure.com/livefree/engineering/_build/latest?definitionId=128\u0026branchName=develop)\n\n\u003e [!WARNING]\n\u003e Version 2 of this package is a complete refactor and is **NOT** backwards compatible.\n\u003e This version targets **net8.0** and has a much easier interface to work with than\n\u003e version 1.\n\n## Installation\n\n##### .Net Core CLI\n\n`dotnet add package OrangeLoop.Sagas`\n\n##### Package Manager Console\n\n`Install-Package OrangeLoop.Sagas`\n\n## How to get started with `IUnitOfWork` (SQL Server)\n\n### Add connection string\n\n```JSON\n// appsettings.json\n\n{\n  \"ConnectionStrings\": {\n    \"MyConnection\": \"Data Source=...\",\n  },\n  ...\n}\n```\n\n### Register `IUnitOfWork` Service\n\n```CSharp\n// Startup.cs or Program.cs\n\nservices.AddSqlServerUnitOfWork(\"MyConnection\", IsolationLevel.ReadUncommitted);\n\n```\n\n### Inject `IConnectionFactory` and `IUnitOfWork` in repository\n\nDatabase queries will need a reference to the current `IDbTransaction`, which can be accessed via the `CurrentTransaction` property of `IUnitOfWork`. Libraries such as Dapper or RepoDB have a `transaction` parameter for this purpose.\n\n#### Example\n\n```CSharp\n// ICustomersRepository.cs\npublic interface ICustomersRepository\n{\n    Task\u003cCustomer\u003e Create(Customer customer);\n    Task\u003cCustomer\u003e Delete(Customer customer);\n    Task\u003cCustomer\u003e FindById(long id);\n}\n\n// CustomersRepository.cs\npublic class CustomersRepository(IConnectionFactory connectionFactory, IUnitOfWork unitOfWork) : ICustomersRepository\n{\n    public async Task\u003cCustomer\u003e FindById(long id)\n    {\n        var conn = connectionFactory.Get();\n        var result = await conn.QueryFirstOrDefaultAsync\u003cCustomer\u003e(\"...\", transaction: unitOfWork.CurrentTransaction);\n    }\n}\n```\n\n\u003e [!NOTE] \u003e `IConnectionFactory` is registered as a Scoped service and implements `IDisposable`.\n\u003e When the scope is disposed (e.g. after an ASP.NET Request) the underlying\n\u003e database connection is closed and properly disposed.\n\n### `IUnitOfWork.ExecuteAsync` (Implicit)\n\nWhen using the implicit option, if no exceptions are thrown, the transaction is committed. If an unhandled exception is thrown, the transaction will be rolled back.\n\n```CSharp\n// CustomersService.cs\n\npublic class CustomersService(IUnitOfWork unitOfWork, ICustomersRepository repo) : ICustomersService\n{\n    public async Task SomeMethod()\n    {\n        await unitOfWork.ExecuteAsync(async () =\u003e\n        {\n            await repo.Create(...);\n            await repo.Create(...);\n            await repo.Delete(...);\n        });\n    }\n}\n```\n\n### `IUnitOfWork.ExecuteAsync` (Explicit)\n\nUsually the implicit option is best, but if you want to handle exceptions within the ExecuteAsync method, then using the explicit option provides that flexibility. Alternatively you can use the implict option and simply rethrow the exception.\n\n```CSharp\n// CustomersService.cs\n\npublic class CustomersService(IUnitOfWork unitOfWork, ICustomersRepository repo) : ICustomersService\n{\n    public async Task SomeMethod()\n    {\n        await unitOfWork.ExecuteAsync(async (success, failure) =\u003e\n        {\n            try\n            {\n                await repo.Create(...);\n                await repo.Create(...);\n                await repo.Delete(...);\n                await success();\n            }\n            catch(Exception e)\n            {\n                // Custom exception handling\n                await failure(e);\n            }\n        });\n    }\n}\n```\n\n\u003e [!WARNING]\n\u003e Failure to call `success` or `failure` when using the explicit option can lead\n\u003e to open database transactions. I will address this in a future update.\n\n## How to get started with `ISaga\u003cT\u003e`\n\n\u003e [!NOTE]\n\u003e Documentation pending. Sample usage available in `SagaTests.cs`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forangeloop%2Fsagas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forangeloop%2Fsagas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forangeloop%2Fsagas/lists"}