{"id":13629217,"url":"https://github.com/igor-tkachev/AspectGenerator","last_synced_at":"2025-04-17T04:33:21.027Z","repository":{"id":208367056,"uuid":"721455918","full_name":"igor-tkachev/AspectGenerator","owner":"igor-tkachev","description":"Tool to create your own aspects.","archived":false,"fork":false,"pushed_at":"2023-12-28T05:14:48.000Z","size":201,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-30T03:54:52.631Z","etag":null,"topics":["aop","aspect","interceptors","interceptslocation","source-generator"],"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/igor-tkachev.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}},"created_at":"2023-11-21T05:10:06.000Z","updated_at":"2024-07-24T22:51:41.000Z","dependencies_parsed_at":"2023-11-27T02:32:51.398Z","dependency_job_id":"dd205928-c366-4f34-9c49-3b57687cc2e5","html_url":"https://github.com/igor-tkachev/AspectGenerator","commit_stats":null,"previous_names":["igor-tkachev/aspect.generator","igor-tkachev/aspectgenerator"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igor-tkachev%2FAspectGenerator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igor-tkachev%2FAspectGenerator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igor-tkachev%2FAspectGenerator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igor-tkachev%2FAspectGenerator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igor-tkachev","download_url":"https://codeload.github.com/igor-tkachev/AspectGenerator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223743217,"owners_count":17195226,"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":["aop","aspect","interceptors","interceptslocation","source-generator"],"created_at":"2024-08-01T22:01:04.833Z","updated_at":"2024-11-08T19:31:39.093Z","avatar_url":"https://github.com/igor-tkachev.png","language":"C#","funding_links":[],"categories":["Content"],"sub_categories":["94. [AspectGenerator](https://ignatandrei.github.io/RSCG_Examples/v2/docs/AspectGenerator) , in the [EnhancementClass](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementclass) category"],"readme":"# Aspect Generator\n\n[![Test workflow](https://img.shields.io/github/actions/workflow/status/igor-tkachev/AspectGenerator/dotnet.yml?branch=master\u0026label=test\u0026logo=github\u0026style=flat-square)](https://github.com/igor-tkachev/AspectGenerator/actions?workflow=.NET) [![NuGet Version and Downloads count](https://buildstats.info/nuget/AspectGenerator?includePreReleases=true)](https://www.nuget.org/packages/AspectGenerator)\n\nThe Aspect Generator can help you easily create your own aspects.\n\n\n\n\n\u003e [!WARNING]\n\u003e [Interceptors](https://github.com/dotnet/roslyn/blob/d71ec683082104e9122a4937abc768710c5f7782/docs/features/interceptors.md) are an experimental compiler feature planned to ship in .NET 8 (with support for C# only).\nThe feature may be subject to breaking changes or removal in a future release.\n\n\u003e [!NOTE]\n\u003e The community still has doubts about the usefulness of this feature. On the one hand, it looks like not kosher fake AOP. On the other hand, it works just fine. This project can help you to try it and share your own opinion.\n\n## Download and Install\n\nInstall nuget\n\n```bash\n\u003e dotnet add package AspectGenerator\n```\n\nModify your project file\n\n```xml\n\u003cPropertyGroup\u003e\n    ...\n    \u003cLangVersion\u003epreview\u003c/LangVersion\u003e\n    \u003cInterceptorsPreviewNamespaces\u003e$(InterceptorsPreviewNamespaces);AspectGenerator\u003c/InterceptorsPreviewNamespaces\u003e\n\n    \u003c!-- Add these settings to specify generated files output path --\u003e\n    \u003cEmitCompilerGeneratedFiles\u003etrue\u003c/EmitCompilerGeneratedFiles\u003e\n    \u003cCompilerGeneratedFilesOutputPath\u003e$(BaseIntermediateOutputPath)\\GeneratedFiles\u003c/CompilerGeneratedFilesOutputPath\u003e\n\u003c/PropertyGroup\u003e\n```\n\n## Read documentation\n\n[How it works](https://github.com/igor-tkachev/AspectGenerator/wiki#how-it-works)\n\n[Creating your own aspect](https://github.com/igor-tkachev/AspectGenerator/wiki#creating-your-own-aspect)\n\n## OpenTelemetry Aspect example\n\nCreate OpenTelemetryFactory and Metrics aspect:\n\n```c#\nusing System;\nusing System.Diagnostics;\n\nusing OpenTelemetry;\nusing OpenTelemetry.Resources;\nusing OpenTelemetry.Trace;\n\nnamespace AspectGenerator\n{\n    /// \u003csummary\u003e\n    /// Initializes OpenTelemetry.\n    /// \u003c/summary\u003e\n    static class OpenTelemetryFactory\n    {\n        public static TracerProvider? Create()\n        {\n            return Sdk.CreateTracerProviderBuilder()\n                .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(\"MySample\"))\n                .AddSource(\"Sample.Aspect\")\n                .AddConsoleExporter()\n                .Build();\n        }\n    }\n\n    /// \u003csummary\u003e\n    /// Metrics aspect.\n    /// \u003c/summary\u003e\n    [Aspect(\n        // Specify the name of the method used in the 'using' statement\n        // that returns an IDisposable object.\n        OnUsing = nameof(OnUsing)\n        )]\n    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]\n    sealed class MetricsAttribute : Attribute\n    {\n        static readonly ActivitySource _activitySource = new(\"Sample.Aspect\");\n\n        public static Activity? OnUsing(InterceptInfo info)\n        {\n            return _activitySource.StartActivity(info.MemberInfo.Name);\n        }\n    }\n}\n```\n\nUse it:\n\n```c#\nusing System;\nusing System.Threading;\n\nusing Aspects;\n\nnamespace OpenTelemetryAspect\n{\n    static class Program\n    {\n        static void Main()\n        {\n            using var _ = OpenTelemetryFactory.Create();\n\n            Method1();\n            Method2();\n            Method1();\n        }\n\n        [Metrics]\n        public static void Method1()\n        {\n            Thread.Sleep(100);\n        }\n\n        [Metrics]\n        public static void Method2()\n        {\n            Thread.Sleep(200);\n        }\n    }\n}\n```\n\nApplication output:\n\n```\nActivity.TraceId:            d47417e726824c7b39055efb4685a9dd\nActivity.SpanId:             12fbf29f5b622e13\nActivity.TraceFlags:         Recorded\nActivity.ActivitySourceName: Sample.Aspect\nActivity.DisplayName:        Method1\nActivity.Kind:               Internal\nActivity.StartTime:          2023-11-22T00:50:15.9079068Z\nActivity.Duration:           00:00:00.1016180\nResource associated with Activity:\n    service.name: MySample\n    service.instance.id: 86dbd377-c850-42a3-b878-be07de30faf1\n    telemetry.sdk.name: opentelemetry\n    telemetry.sdk.language: dotnet\n    telemetry.sdk.version: 1.6.0\n\nActivity.TraceId:            b90735bfb52cb0b52a504d02bc5ead2e\nActivity.SpanId:             75109ef3af25a3e9\nActivity.TraceFlags:         Recorded\nActivity.ActivitySourceName: Sample.Aspect\nActivity.DisplayName:        Method2\nActivity.Kind:               Internal\nActivity.StartTime:          2023-11-22T00:50:16.0360160Z\nActivity.Duration:           00:00:00.2058166\nResource associated with Activity:\n    service.name: MySample\n    service.instance.id: 86dbd377-c850-42a3-b878-be07de30faf1\n    telemetry.sdk.name: opentelemetry\n    telemetry.sdk.language: dotnet\n    telemetry.sdk.version: 1.6.0\n\nActivity.TraceId:            e9653008f381b6330a8e538e02b7a61d\nActivity.SpanId:             be3d7cd1d4376bd7\nActivity.TraceFlags:         Recorded\nActivity.ActivitySourceName: Sample.Aspect\nActivity.DisplayName:        Method1\nActivity.Kind:               Internal\nActivity.StartTime:          2023-11-22T00:50:16.2517480Z\nActivity.Duration:           00:00:00.1135186\nResource associated with Activity:\n    service.name: MySample\n    service.instance.id: 86dbd377-c850-42a3-b878-be07de30faf1\n    telemetry.sdk.name: opentelemetry\n    telemetry.sdk.language: dotnet\n    telemetry.sdk.version: 1.6.0\n```\n\nGenerated code:\n\n```c#\n// \u003cauto-generated/\u003e\n#pragma warning disable\n#nullable enable\n\nusing System;\n\nusing SR  = System.Reflection;\nusing SLE = System.Linq.Expressions;\nusing SCG = System.Collections.Generic;\n\nnamespace Aspects\n{\n    static partial class Interceptors\n    {\n        static SR.MethodInfo GetMethodInfo(SLE.Expression expr)\n        {\n            return expr switch\n            {\n                SLE.MethodCallExpression mc =\u003e mc.Method,\n                _                           =\u003e throw new InvalidOperationException()\n            };\n        }\n\n        static SR.MethodInfo MethodOf\u003cT\u003e(SLE.Expression\u003cFunc\u003cT\u003e\u003e func) =\u003e GetMethodInfo(func.Body);\n        static SR.MethodInfo MethodOf   (SLE.Expression\u003cAction\u003e  func) =\u003e GetMethodInfo(func.Body);\n\n        static SR. MemberInfo                 Method1_Interceptor_MemberInfo        = MethodOf(() =\u003e OpenTelemetryAspect.Program.Method1());\n        static SCG.Dictionary\u003cstring,object?\u003e Method1_Interceptor_AspectArguments_0 = new ()\n        {\n        };\n        //\n        /// \u003csummary\u003e\n        /// Intercepts OpenTelemetryAspect.Program.Method1().\n        /// \u003c/summary\u003e\n        //\n        // Intercepts Method1().\n        [System.Runtime.CompilerServices.InterceptsLocation(@\"P:\\AspectGenerator\\Examples\\OpenTelemetryAspect\\Program.cs\", line: 14, character: 4)]\n        //\n        // Intercepts Method1().\n        [System.Runtime.CompilerServices.InterceptsLocation(@\"P:\\AspectGenerator\\Examples\\OpenTelemetryAspect\\Program.cs\", line: 16, character: 4)]\n        //\n        [System.Runtime.CompilerServices.CompilerGenerated]\n        //[System.Diagnostics.DebuggerStepThrough]\n        public static void Method1_Interceptor()\n        {\n            // Aspects.MetricsAttribute\n            //\n            var __info__0 = new Aspects.InterceptInfo\u003cVoid\u003e\n            {\n                MemberInfo      = Method1_Interceptor_MemberInfo,\n                AspectType      = typeof(Aspects.MetricsAttribute),\n                AspectArguments = Method1_Interceptor_AspectArguments_0,\n            };\n\n            using (Aspects.MetricsAttribute.OnUsing(__info__0))\n            {\n                OpenTelemetryAspect.Program.Method1();\n            }\n        }\n\n        static SR. MemberInfo                 Method2_Interceptor_MemberInfo        = MethodOf(() =\u003e OpenTelemetryAspect.Program.Method2());\n        static SCG.Dictionary\u003cstring,object?\u003e Method2_Interceptor_AspectArguments_0 = new ()\n        {\n        };\n        //\n        /// \u003csummary\u003e\n        /// Intercepts OpenTelemetryAspect.Program.Method2().\n        /// \u003c/summary\u003e\n        //\n        // Intercepts Method2().\n        [System.Runtime.CompilerServices.InterceptsLocation(@\"P:\\AspectGenerator\\Examples\\OpenTelemetryAspect\\Program.cs\", line: 15, character: 4)]\n        //\n        [System.Runtime.CompilerServices.CompilerGenerated]\n        //[System.Diagnostics.DebuggerStepThrough]\n        public static void Method2_Interceptor()\n        {\n            // Aspects.MetricsAttribute\n            //\n            var __info__0 = new Aspects.InterceptInfo\u003cVoid\u003e\n            {\n                MemberInfo      = Method2_Interceptor_MemberInfo,\n                AspectType      = typeof(Aspects.MetricsAttribute),\n                AspectArguments = Method2_Interceptor_AspectArguments_0,\n            };\n\n            using (Aspects.MetricsAttribute.OnUsing(__info__0))\n            {\n                OpenTelemetryAspect.Program.Method2();\n            }\n        }\n    }\n}\n```\n\nMore advanced version of the Metrics aspect can also set activity status and support `await using`.\n\n```c#\n[Aspect(\n    OnUsing      = nameof(OnUsing),\n    OnAsyncUsing = nameof(OnAsyncUsing),\n    OnFinally    = nameof(OnFinally)\n    )]\n[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]\nsealed class MetricsAttribute : Attribute\n{\n    static readonly ActivitySource _activitySource = new(\"Sample.Aspect\");\n\n    public static Activity? OnUsing(InterceptInfo info)\n    {\n        var activity = _activitySource.StartActivity(info.MemberInfo.Name);\n\n        info.Tag = activity;\n\n        return activity;\n    }\n\n    class AsyncActivity(Activity activity) : IAsyncDisposable\n    {\n        public readonly Activity Activity = activity;\n\n        public ValueTask DisposeAsync()\n        {\n            Activity.Dispose();\n            return ValueTask.CompletedTask;\n        }\n    }\n\n    public static IAsyncDisposable? OnAsyncUsing(InterceptInfo info)\n    {\n        var activity = _activitySource.StartActivity(info.MemberInfo.Name);\n\n        if (activity == null)\n            return null;\n\n        var asyncActivity = new AsyncActivity(activity);\n\n        info.Tag = asyncActivity;\n\n        return asyncActivity;\n    }\n\n    public static void OnFinally(InterceptInfo info)\n    {\n        switch (info)\n        {\n            case { Tag: Activity activity, Exception: var ex } : SetStatus(activity,    ex); break;\n            case { Tag: AsyncActivity aa,  Exception: var ex } : SetStatus(aa.Activity, ex); break;\n        }\n\n        static void SetStatus(Activity activity, Exception? ex) =\u003e\n            activity.SetStatus(ex is null ? ActivityStatusCode.Ok : ActivityStatusCode.Error);\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figor-tkachev%2FAspectGenerator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figor-tkachev%2FAspectGenerator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figor-tkachev%2FAspectGenerator/lists"}