{"id":26800609,"url":"https://github.com/ktsu-dev/scopedaction","last_synced_at":"2026-02-16T05:04:11.948Z","repository":{"id":231621077,"uuid":"781168905","full_name":"ktsu-dev/ScopedAction","owner":"ktsu-dev","description":null,"archived":false,"fork":false,"pushed_at":"2026-01-30T02:53:11.000Z","size":381,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-30T17:56:00.371Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PowerShell","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/ktsu-dev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":"COPYRIGHT.md","agents":null,"dco":null,"cla":null}},"created_at":"2024-04-02T21:48:27.000Z","updated_at":"2026-01-30T02:50:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"eebff89c-ed40-42e8-8e76-1047748ab169","html_url":"https://github.com/ktsu-dev/ScopedAction","commit_stats":null,"previous_names":["ktsu-io/scopedaction","ktsu-dev/scopedaction"],"tags_count":110,"template":false,"template_full_name":null,"purl":"pkg:github/ktsu-dev/ScopedAction","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FScopedAction","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FScopedAction/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FScopedAction/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FScopedAction/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ktsu-dev","download_url":"https://codeload.github.com/ktsu-dev/ScopedAction/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ktsu-dev%2FScopedAction/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29136034,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T21:59:57.939Z","status":"ssl_error","status_checked_at":"2026-02-05T21:59:57.628Z","response_time":65,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":"2025-03-29T20:18:06.757Z","updated_at":"2026-02-05T22:07:23.854Z","avatar_url":"https://github.com/ktsu-dev.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ktsu.ScopedAction\n\n\u003e A lightweight utility for executing paired actions at the start and end of code blocks.\n\n[![License](https://img.shields.io/github/license/ktsu-dev/ScopedAction)](https://github.com/ktsu-dev/ScopedAction/blob/main/LICENSE.md)\n[![NuGet](https://img.shields.io/nuget/v/ktsu.ScopedAction.svg)](https://www.nuget.org/packages/ktsu.ScopedAction/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.ScopedAction.svg)](https://www.nuget.org/packages/ktsu.ScopedAction/)\n[![Build Status](https://github.com/ktsu-dev/ScopedAction/workflows/build/badge.svg)](https://github.com/ktsu-dev/ScopedAction/actions)\n[![GitHub Stars](https://img.shields.io/github/stars/ktsu-dev/ScopedAction?style=social)](https://github.com/ktsu-dev/ScopedAction/stargazers)\n\n## Introduction\n\n`ktsu.ScopedAction` is a .NET utility that provides an abstract base class for executing actions at the beginning and end of code blocks. It implements the RAII (Resource Acquisition Is Initialization) pattern and leverages C#'s `using` statement and the `IDisposable` pattern to ensure that paired operations (like resource acquisition/release, state changes, or logging) are properly executed, even in the presence of exceptions.\n\nAs an abstract class, `ScopedAction` is designed to be inherited and extended to create specialized scoped behavior classes tailored to specific use cases.\n\n## Features\n\n- **Abstract Base Class**: Provides a foundation for creating specialized scoped action classes\n- **RAII Pattern**: Implements Resource Acquisition Is Initialization for deterministic resource management\n- **Paired Actions**: Execute actions when entering and exiting a scope\n- **Exception Safety**: Cleanup actions execute even if exceptions occur\n- **Lightweight**: Simple API with minimal overhead\n- **Inheritance-Based**: Designed to be extended for domain-specific implementations\n- **Flexible**: Works with any action delegates through protected constructor\n- **Resource Management**: Follows .NET's standard disposal pattern\n\n## RAII (Resource Acquisition Is Initialization)\n\n`ktsu.ScopedAction` implements the RAII pattern, a programming idiom that binds the life cycle of a resource to the lifetime of an object. This ensures that:\n\n- **Automatic Resource Management**: Resources are automatically acquired when the object is constructed and released when it's destroyed\n- **Exception Safety**: Resources are properly released even if exceptions occur within the scope\n- **Deterministic Execution**: The OnClose action is guaranteed to execute when the object goes out of scope\n- **Stack-Based Semantics**: Leverages C#'s `using` statement to provide execution tied to lexical scope, mimicking C++ stack-based object destruction\n\nThe pattern is particularly useful for scenarios like:\n- File operations (open/close)\n- Database transactions (begin/commit or rollback)\n- Lock management (acquire/release)\n- Performance timing (start/stop)\n- Temporary state changes (set/restore)\n\n## Installation\n\n### Package Manager Console\n\n```powershell\nInstall-Package ktsu.ScopedAction\n```\n\n### .NET CLI\n\n```bash\ndotnet add package ktsu.ScopedAction\n```\n\n### Package Reference\n\n```xml\n\u003cPackageReference Include=\"ktsu.ScopedAction\" Version=\"x.y.z\" /\u003e\n```\n\n## Usage Examples\n\nThe `ScopedAction` class supports three main patterns, progressing from simple to more complex scenarios:\n\n### Example 1: Using static methods without parameters\n\n```csharp\nusing ktsu.ScopedAction;\n\npublic class ConsoleMarkerScope() : ScopedAction(Enter, Exit)\n{\n    // Using method groups - no lambdas needed when methods match Action signature\n    private static void Enter() =\u003e Console.WriteLine(\"Entering scope\");\n    private static void Exit() =\u003e Console.WriteLine(\"Exiting scope\");\n}\n\n// Usage\nusing (new ConsoleMarkerScope())\n{\n    // Any code here...\n    Console.WriteLine(\"Inside the scope\");\n}\n\n// Output:\n// Entering scope\n// Inside the scope\n// Exiting scope\n```\n\n### Example 2: Using static methods with parameters\n\n```csharp\nusing ktsu.ScopedAction;\n\npublic class LoggingScope(string operation)\n    : ScopedAction(() =\u003e Enter(operation), () =\u003e Exit(operation))\n{\n    // Using lambdas to capture constructor parameters for static methods\n    private static void Enter(string operation) =\u003e Console.WriteLine($\"Entering: {operation}\");\n    private static void Exit(string operation) =\u003e Console.WriteLine($\"Exiting: {operation}\");\n}\n\n// Usage\nusing (new LoggingScope(\"my operation\"))\n{\n    // Any code here...\n    Console.WriteLine(\"Inside the scope\");\n}\n\n// Output:\n// Entering: my operation\n// Inside the scope\n// Exiting: my operation\n```\n\n### Example 3: Using instance members\n\n```csharp\nusing ktsu.ScopedAction;\n\n// This approach enables access to instance members in the OnClose action\npublic class TimingScope : ScopedAction\n{\n    private readonly DateTime startTime;  // Instance field\n    private readonly string operation;    // Instance field\n\n    public TimingScope(string operation)\n    {\n        this.operation = operation;\n        this.startTime = DateTime.Now;\n\n        // OnClose can reference instance method that accesses instance members\n        OnClose = LogExecutionTime;\n\n        // No need to assign an OnOpen action - it would execute immediately anyway.\n        // Instead, just perform the \"on open\" logic directly in the constructor.\n        Console.WriteLine($\"Starting: {operation}\");\n    }\n\n    // Instance method with access to instance fields\n    private void LogExecutionTime()\n    {\n        // Can directly access instance members: startTime, operation\n        var elapsed = DateTime.Now - startTime;\n        Console.WriteLine($\"Completed: {operation} in {elapsed.TotalMilliseconds:F2}ms\");\n    }\n}\n\n// Usage\nusing (new TimingScope(\"database query\"))\n{\n    // Simulate some work\n    Thread.Sleep(100);\n    Console.WriteLine(\"Executing query...\");\n}\n\n// Output:\n// Starting: database query\n// Executing query...\n// Completed: database query in 100.xx ms\n```\n\n#### Choosing the Right Pattern\n\n**Example 1 (Method Groups)**: Use when you have simple static methods with no parameters. This is the most concise approach.\n\n**Example 2 (Lambda Capture)**: Use when you need to pass constructor parameters to static methods. Lambdas capture the parameters from the constructor scope.\n\n**Example 3 (Instance Members)**: Use when your OnClose logic needs access to **instance state** (fields, properties, methods). This pattern is essential for:\n- Complex resource management\n- Stateful cleanup operations  \n- Scenarios where disposal behavior depends on data initialized during construction\n\nThe parameterless constructor approach gives you full access to the object's state, while the action-based constructors are limited to static methods and captured parameters.\n\n## API Reference\n\n### ScopedAction Class\n\nAn abstract base class for executing actions at scope boundaries. This class must be inherited to create concrete implementations.\n\n#### Constructors\n\n| Constructor | Parameters | Description |\n|-------------|------------|-------------|\n| `ScopedAction(Action? onOpen, Action? onClose)` | `onOpen`: Action executed on construction\u003cbr\u003e`onClose`: Action executed on disposal | Protected constructor for derived classes that executes the onOpen action immediately and stores the onClose action for later execution during disposal |\n| `ScopedAction()` | None | Protected parameterless constructor for derived classes that need custom initialization |\n\n#### Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `OnClose` | `Action?` | Protected property that stores the action to execute when the scoped action is disposed. Can be set by derived classes. |\n\n#### Methods\n\n| Method | Return Type | Description |\n|--------|-------------|-------------|\n| `Dispose()` | `void` | Public method that implements the IDisposable interface. Executes the OnClose action if not already disposed and suppresses finalization. |\n| `Dispose(bool disposing)` | `void` | Protected virtual method for implementing the standard .NET dispose pattern. Executes the OnClose action when disposing is true and handles multiple disposal calls safely. |\n\n## Contributing\n\nContributions are welcome! Here's how you can help:\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\nPlease make sure to update tests as appropriate.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsu-dev%2Fscopedaction","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fktsu-dev%2Fscopedaction","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fktsu-dev%2Fscopedaction/lists"}