{"id":13629394,"url":"https://github.com/Sholtee/ProxyGen","last_synced_at":"2025-04-17T09:33:31.430Z","repository":{"id":62734737,"uuid":"232097905","full_name":"Sholtee/proxygen","owner":"Sholtee","description":".NET proxy generator powered by Roslyn","archived":false,"fork":false,"pushed_at":"2025-04-17T02:00:23.000Z","size":2675,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-17T02:31:17.755Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/proxygen.net","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/Sholtee.png","metadata":{"files":{"readme":"README.MD","changelog":"history.md","contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2020-01-06T12:38:59.000Z","updated_at":"2025-01-26T11:11:00.000Z","dependencies_parsed_at":"2023-12-27T11:23:20.959Z","dependency_job_id":"9bbcf46e-8b86-42bb-bcae-49d6b69565da","html_url":"https://github.com/Sholtee/proxygen","commit_stats":null,"previous_names":[],"tags_count":68,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sholtee%2Fproxygen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sholtee%2Fproxygen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sholtee%2Fproxygen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sholtee%2Fproxygen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sholtee","download_url":"https://codeload.github.com/Sholtee/proxygen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249331634,"owners_count":21252621,"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-08-01T22:01:09.298Z","updated_at":"2025-04-17T09:33:31.416Z","avatar_url":"https://github.com/Sholtee.png","language":"C#","funding_links":[],"categories":["Content"],"sub_categories":["36. [ProxyGen](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ProxyGen) , in the [Interface](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#interface) category"],"readme":"# ProxyGen.NET [![Build status](https://ci.appveyor.com/api/projects/status/caw7qqtf5tbaa1fq/branch/master?svg=true)](https://ci.appveyor.com/project/Sholtee/proxygen/branch/master) ![AppVeyor tests](https://img.shields.io/appveyor/tests/sholtee/proxygen/master) [![Coverage Status](https://coveralls.io/repos/github/Sholtee/proxygen/badge.svg?branch=master)](https://coveralls.io/github/Sholtee/proxygen?branch=master) [![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/proxygen.net)](https://www.nuget.org/packages/proxygen.net) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sholtee/proxygen/master)\n\u003e .NET proxy generator powered by [Roslyn](https://github.com/dotnet/roslyn )\n\n**This documentation refers the version 9.X of the library**\n## Purposes\nThis library currently supports generating [proxies](https://en.wikipedia.org/wiki/Proxy_pattern ) for interface interception and [duck typing](https://en.wikipedia.org/wiki/Duck_typing ).\n### To hook into interface method calls:\n1. Create the interceptor class (which is an [InterfaceInterceptor](https://sholtee.github.io/proxygen/doc/Solti.Utils.Proxy.InterfaceInterceptor-1.html ) descendant):\n  ```csharp\n  using Solti.Utils.Proxy;\n  ...\n  public class MyInterceptor: InterfaceInterceptor\u003cIMyInterface\u003e\n  {\n    public MyInterceptor(IMyInterface target) : base(target) {}\n\n    public MyInterceptor(IMyInterface target, MyParam myParam) : base(target) {}  // overloaded constructor\n\n    public override object? Invoke(InvocationContext context) // Invoking the generated proxy instance will trigger this method\n    {\n\t  if (suppressOriginalMethod)\n\t  {\n\t    return something;\n        // ref|out parameters can be assigned by setting the corresponding \"context.Args[]\" item \n\t  }\n\t  \n\t  context.Args[0] = someNewVal; // \"someNewVal\" will be forwarded to the original method\n\t  \n\t  return base.Invoke(context); // Let the original method do its work\n    }  \n  }\n  // OR\n  public class MyInterceptorTargetingTheImplementation: InterfaceInterceptor\u003cIMyInterface, MyInterfaceImplementation\u003e\n  {\n      public MyInterceptor(MyInterfaceImplementation target) : base(target) {}\n\n      public override object? Invoke(InvocationContext context)\n      {\n          MemberInfo\n              ifaceMember  = context.InterfaceMember,  // Will point to the invoked IMyInterface member (e.g.: IMyInterface.Foo())\n              targetMember = context.TargetMember; // Will point to the underlying MyInterfaceImplementation member (e.g. MyInterfaceImplementation.Foo())\n\n          return base.Invoke(context);\n      }\n  }\n  ```\n2. Generate a proxy instance invoking the desired constructor:\n  ```csharp\n  using System;\n  ...\n  IMyInterface target = new MyClass();\n  ...\n  IMyInterface proxy;\n  \n  proxy = ProxyGenerator\u003cIMyInterface, MyInterceptor\u003e.Activate(Tuple.Create(target)); // or ActivateAsync()\n  proxy = ProxyGenerator\u003cIMyInterface, MyInterceptor\u003e.Activate(Tuple.Create(target, new MyParam()));\n  ```\n3. Enjoy\n\nRemarks:\n- The *target* can access its most outer enclosing proxy. To achieve this it just has to implement the `IProxyAccess\u003cIMyInterface\u003e` interface:\n  ```csharp\n  using Solti.Utils.Proxy;\n\n  public class MyClass : IMyInterface, IProxyAccess\u003cIMyInterface\u003e\n  {\n      ...\n      public IMyInterface Proxy { get; set; }\n  }\n  ```\n- Starting from v9.1 partial interface implementations are also supported:\n  ```csharp\n  using Solti.Utils.Proxy;\n\n  public interface IMyInterface\n  {\n      void Intercepted();\n      void NotInterceptred();\n  } \n\n  public class MyInterceptor: InterfaceInterceptor\u003cIMyInterface\u003e\n  {\n      public void NotInterceptred() {...}\n\n      // will be triggered by Intercepted() only as NotInterceptred() has its own implementation\n      public override object Invoke(InvocationContext context) {...}\n\n      ...\n  }\n  ```\n\nFor further usage examples see [this](https://github.com/Sholtee/proxygen/blob/master/TEST/ProxyGen.Tests/Generators/ProxyGenerator.cs ) or [that](https://github.com/Sholtee/injector#decorating-services ).\n### To create ducks:\n1. Declare an interface that covers all the desired members of the target class:\n  ```csharp\n  public class TargetClass // does not implement IDuck\n  {\n    public void Foo() {...}\n  }\n  ...\n  public interface IDuck \n  {\n    void Foo();\n  }\n  ```\n2. Generate the duck instance:\n  ```csharp\n  using Solti.Utils.Proxy.Generators;\n  ...\n  TargetClass target = ...;\n  IDuck duck = DuckGenerator\u003cIDuck, TargetClass\u003e.Activate(Tuple.Create(target)); // or ActivateAsync()\n  ```\n3. Quack\n  \nRelated tests can be seen [here](https://github.com/Sholtee/proxygen/blob/master/TEST/ProxyGen.Tests/Generators/DuckGenerator.cs ).\n## Caching the generated assembly\nBy setting the `ProxyGen.AssemblyCacheDir` property in [YourApp.runtimeconfig.json](https://docs.microsoft.com/en-us/dotnet/core/run-time-config/ ) you can make the system cache the generated assembly, so next time your app starts and requests the proxy there won't be time consuming emitting operation.\n\nYou can do it easily by creating a template file named `runtimeconfig.template.json` in your project folder:\n```json\n{\n  \"configProperties\": {\n    \"ProxyGen.AssemblyCacheDir\": \"GeneratedAssemblies\"\n  }\n}\n```\n## Embedding the generated type\nThis library can be used as a [source generator](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/ ) so you can embed the generated proxy type into the assembly that uses it. This is simply done by the `Solti.Utils.Proxy.Attributes.EmbedGeneratedTypeAttribute`:\n```csharp\n[assembly: EmbedGeneratedType(typeof(ProxyGenerator\u003cIMyInterface, MyInterceptor\u003cIMyInterface\u003e\u003e))]\n[assembly: EmbedGeneratedType(typeof(DuckGenerator\u003cIMyInterface, MyClass\u003e))]\n\n```\nThe `xXxGenerator.GetGeneratedType()` method returns the embedded type if it is present in the assembly in which the `GetGeneratedType()` was called. Since all the time consuming operations already happened in compile time, requesting embedded types can singificantly improve the performance.\n\nNote that:\n- Open generics are not supported.\n- [coveralls.io](https://www.nuget.org/packages/coveralls.io/ ) (and other coverage reporters) may crash if your project was augmented by a source generator. To work this issue around:\n  - Ignore the generated sources in your coverage app (e.g.: in [OpenCover](https://www.nuget.org/packages/OpenCover/ ) use the `-filter:-[*]Proxies.GeneratedClass_*` switch)\n  - Create an empty file for each generated class (e.g.: `YourProject\\Solti.Utils.Proxy\\Solti.Utils.Proxy.Internals.ProxyEmbedder\\Proxies.GeneratedClass_XxX.cs`)\n  - Exclude these files from your project:\n  ```xml\n  \u003cItemGroup\u003e\n    \u003cCompile Remove=\"Solti.Utils.Proxy\\**\" /\u003e\n    \u003cEmbeddedResource Remove=\"Solti.Utils.Proxy\\**\" /\u003e\n    \u003cNone Remove=\"Solti.Utils.Proxy\\**\" /\u003e\n  \u003c/ItemGroup\u003e\n  ```  \n## Inspecting the generated code\n*ProxyGen* is able to dump the generated sources. Due to performance considerations it is disabled by default. To enable \n- In runtime:\n\n  Set the `ProxyGen.SourceDump` property (in the same way you could see [above](#caching-the-generated-assembly)) to the desired directory (note that environment variables are supported):\n  ```json\n  {\n    \"configProperties\": {\n      \"ProxyGen.SourceDump\": \"%TEMP%\"\n    }\n  }\n  ```\n  \n- In compile time (source generator):\n\n  Extend your `.csproj` with the following:\n  ```xml\n  \u003cPropertyGroup\u003e\n    \u003cProxyGen_SourceDump\u003e$(OutputPath)Logs\u003c/ProxyGen_SourceDump\u003e\n  \u003c/PropertyGroup\u003e\n  ```\n\nThe output should look like [this](https://github.com/Sholtee/proxygen/blob/master/TEST/ProxyGen.Tests/ClsSrcUnit.txt ).\n## Migrating from version \n- 2.X\n  - Delete all the cached assemblies (if the `[Proxy|Duck]Generator.CacheDirectory` is set somewhere)\n  - `InterfaceInterceptor.Invoke()` returns the result of the original method (instead of `CALL_TARGET`) so in the override you may never need to invoke the `method` parameter directly.\n- 3.X\n  - `[Proxy|Duck]Generator.GeneratedType[Async]` property has been removed. To get the generated proxy type call the `[Proxy|Duck]Generator.GetGeneratedType[Async]()` method.\n  - `[Proxy|Duck]Generator.CacheDirectory` property has been removed. To set the cache directory tweak the [runtimeconfig.json](#caching-the-generated-assembly) file.\n- 4.X\n  - The layout of the `InterfaceInterceptor\u003c\u003e.Invoke()` has been changed. Invocation parameters can be grabbed from the `InvocationContext` passed to the `Invoke()` method.\n  - The `ConcurrentInterfaceInterceptor\u003c\u003e` class has been dropped since the `InterfaceInterceptor\u003c\u003e` class was rewritten in a thread safe manner.\n- 5.X\n  - You don't need to manually activate the generated proxy type, instead you may use the built-in `Generator.Activate()` method.\n- 6.X\n  - The `InvocationContext.InvokeTarget` property has been removed but you should not be affected by it\n  - As proxy embedder has been reimplemented using the [v2](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md ) Source Generator API, this feature now requires VS 2022\n- 7.X\n  - `InterfaceInterceptor\u003cTInterface\u003e.Member|Method` has been renamed to `InterfaceMember|InterfaceMethod`\n- 8.X\n  - `Generator`s have been demoted to `class`. To compare `Generator` instances use their `Id` property.\n## Resources\n- [API Docs](https://sholtee.github.io/proxygen )\n- [Benchmark Results](https://sholtee.github.io/proxygen/perf )\n- [Version History](https://github.com/Sholtee/proxygen/blob/master/history.md )\n\n## Supported frameworks\nThis project currently targets `netstandard2.0` as well as `netstandard2.1` and had been tested against `net472`, `netcoreapp3.1`, `net5.0`, `net6.0`, `net7.0`, `net8.0` and `net9.0`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSholtee%2FProxyGen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSholtee%2FProxyGen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSholtee%2FProxyGen/lists"}