diff --git a/README.md b/README.md index 2a821e9e3..09faafa44 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# RSCG - 228 Examples of Roslyn Source Code Generators / 14 created by Microsoft / +# RSCG - 229 Examples of Roslyn Source Code Generators / 14 created by Microsoft / -## Latest Update : 2025-08-16 => 16 August 2025 +## Latest Update : 2025-08-17 => 17 August 2025 If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)*** @@ -20,8 +20,30 @@ If you want to be notified each time I add a new RSCG example , please click htt ## Content -Those are the 228 Roslyn Source Code Generators that I have tested you can see and download source code example. +Those are the 229 Roslyn Source Code Generators that I have tested you can see and download source code example. ( including 14 from Microsoft ) +### 229. [Facet](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet) , in the [Mapper](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#mapper) category + +Generated on : 2025-08-17 => 17 August 2025 + +
+ Expand + + + +Author: Tim Maes + +Generate lean DTOs, slim views, or faceted projections of your models with a single attribute. + +Nuget: [https://www.nuget.org/packages/Facet/](https://www.nuget.org/packages/Facet/) + + +Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet) + +Source: [https://github.com/Tim-Maes/Facet/](https://github.com/Tim-Maes/Facet/) + +
+ ### 228. [mvvmgen](https://ignatandrei.github.io/RSCG_Examples/v2/docs/mvvmgen) , in the [Serializer](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#serializer) category Generated on : 2025-08-16 => 16 August 2025 @@ -5473,182 +5495,175 @@ Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=https://github.com/TheFo2sh/AsyncFlow&body=https://github.com/TheFo2sh/AsyncFlow -39) [https://github.com/Tim-Maes/Facet]( https://github.com/Tim-Maes/Facet) , https://github.com/Tim-Maes/Facet - -Why I have not tested : later - -https://github.com/ignatandrei/RSCG_Examples/issues/new?title=https://github.com/Tim-Maes/Facet&body=https://github.com/Tim-Maes/Facet - - -40) [https://github.com/wieslawsoltes/ReactiveGenerator]( https://github.com/wieslawsoltes/ReactiveGenerator) , https://github.com/wieslawsoltes/ReactiveGenerator +39) [https://github.com/wieslawsoltes/ReactiveGenerator]( https://github.com/wieslawsoltes/ReactiveGenerator) , https://github.com/wieslawsoltes/ReactiveGenerator Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=https://github.com/wieslawsoltes/ReactiveGenerator&body=https://github.com/wieslawsoltes/ReactiveGenerator -41) [Intellenum]( https://github.com/SteveDunn/Intellenum) , https://github.com/SteveDunn/Intellenum +40) [Intellenum]( https://github.com/SteveDunn/Intellenum) , https://github.com/SteveDunn/Intellenum Why I have not tested : not understand how to use https://github.com/ignatandrei/RSCG_Examples/issues/new?title=Intellenum&body=https://github.com/SteveDunn/Intellenum -42) [laker]( https://github.com/Lakerfield/Lakerfield.Rpc) , https://github.com/Lakerfield/Lakerfield.Rpc +41) [laker]( https://github.com/Lakerfield/Lakerfield.Rpc) , https://github.com/Lakerfield/Lakerfield.Rpc Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=laker&body=https://github.com/Lakerfield/Lakerfield.Rpc -43) [LoggingDecoratorGenerator]( https://github.com/DavidFineboym/LoggingDecoratorGenerator) , https://github.com/DavidFineboym/LoggingDecoratorGenerator +42) [LoggingDecoratorGenerator]( https://github.com/DavidFineboym/LoggingDecoratorGenerator) , https://github.com/DavidFineboym/LoggingDecoratorGenerator Why I have not tested : Microsoft have done same feature https://github.com/ignatandrei/RSCG_Examples/issues/new?title=LoggingDecoratorGenerator&body=https://github.com/DavidFineboym/LoggingDecoratorGenerator -44) [lucide-blazor]( https://github.com/brecht-vde/lucide-blazor/) , https://github.com/brecht-vde/lucide-blazor/ +43) [lucide-blazor]( https://github.com/brecht-vde/lucide-blazor/) , https://github.com/brecht-vde/lucide-blazor/ Why I have not tested : issue opened https://github.com/ignatandrei/RSCG_Examples/issues/new?title=lucide-blazor&body=https://github.com/brecht-vde/lucide-blazor/ -45) [ManagedDotnetProfiler]( https://github.com/kevingosse/ManagedDotnetProfiler) , https://github.com/kevingosse/ManagedDotnetProfiler +44) [ManagedDotnetProfiler]( https://github.com/kevingosse/ManagedDotnetProfiler) , https://github.com/kevingosse/ManagedDotnetProfiler Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=ManagedDotnetProfiler&body=https://github.com/kevingosse/ManagedDotnetProfiler -46) [Maui.BindableProperty.Generator]( https://github.com/rrmanzano/maui-bindableproperty-generator) , https://github.com/rrmanzano/maui-bindableproperty-generator +45) [Maui.BindableProperty.Generator]( https://github.com/rrmanzano/maui-bindableproperty-generator) , https://github.com/rrmanzano/maui-bindableproperty-generator Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=Maui.BindableProperty.Generator&body=https://github.com/rrmanzano/maui-bindableproperty-generator -47) [Minerals.AutoCQRS]( https://github.com/SzymonHalucha/Minerals.AutoCQRS) , https://github.com/SzymonHalucha/Minerals.AutoCQRS +46) [Minerals.AutoCQRS]( https://github.com/SzymonHalucha/Minerals.AutoCQRS) , https://github.com/SzymonHalucha/Minerals.AutoCQRS Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=Minerals.AutoCQRS&body=https://github.com/SzymonHalucha/Minerals.AutoCQRS -48) [Minerals.AutoDomain]( https://github.com/SzymonHalucha/Minerals.AutoDomain) , https://github.com/SzymonHalucha/Minerals.AutoDomain +47) [Minerals.AutoDomain]( https://github.com/SzymonHalucha/Minerals.AutoDomain) , https://github.com/SzymonHalucha/Minerals.AutoDomain Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=Minerals.AutoDomain&body=https://github.com/SzymonHalucha/Minerals.AutoDomain -49) [observable]( https://github.com/notanaverageman/Bindables) , https://github.com/notanaverageman/Bindables +48) [observable]( https://github.com/notanaverageman/Bindables) , https://github.com/notanaverageman/Bindables Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=observable&body=https://github.com/notanaverageman/Bindables -50) [PolySharp]( https://github.com/Sergio0694/PolySharp) , https://github.com/Sergio0694/PolySharp +49) [PolySharp]( https://github.com/Sergio0694/PolySharp) , https://github.com/Sergio0694/PolySharp Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=PolySharp&body=https://github.com/Sergio0694/PolySharp -51) [RazorGen]( https://github.com/dartk/RazorGen) , https://github.com/dartk/RazorGen +50) [RazorGen]( https://github.com/dartk/RazorGen) , https://github.com/dartk/RazorGen Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=RazorGen&body=https://github.com/dartk/RazorGen -52) [SerdeDn]( https://github.com/serdedotnet/serde) , https://github.com/serdedotnet/serde +51) [SerdeDn]( https://github.com/serdedotnet/serde) , https://github.com/serdedotnet/serde Why I have not tested : serializer. Done by MSFT with System.Text.Json https://github.com/ignatandrei/RSCG_Examples/issues/new?title=SerdeDn&body=https://github.com/serdedotnet/serde -53) [SogePoco]( https://github.com/d-p-y/SogePoco) , https://github.com/d-p-y/SogePoco +52) [SogePoco]( https://github.com/d-p-y/SogePoco) , https://github.com/d-p-y/SogePoco Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=SogePoco&body=https://github.com/d-p-y/SogePoco -54) [SourceCrafter.HttpServiceClientGenerator]( https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/) , https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ +53) [SourceCrafter.HttpServiceClientGenerator]( https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/) , https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=SourceCrafter.HttpServiceClientGenerator&body=https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ -55) [ST.NSwag.ServerSourceGenerator]( https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator) , https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator +54) [ST.NSwag.ServerSourceGenerator]( https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator) , https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=ST.NSwag.ServerSourceGenerator&body=https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator -56) [StrongInject]( https://github.com/YairHalberstadt/stronginject/) , https://github.com/YairHalberstadt/stronginject/ +55) [StrongInject]( https://github.com/YairHalberstadt/stronginject/) , https://github.com/YairHalberstadt/stronginject/ Why I have not tested : later https://github.com/ignatandrei/RSCG_Examples/issues/new?title=StrongInject&body=https://github.com/YairHalberstadt/stronginject/ -57) [TeuJson]( https://github.com/Terria-K/TeuJson) , https://github.com/Terria-K/TeuJson +56) [TeuJson]( https://github.com/Terria-K/TeuJson) , https://github.com/Terria-K/TeuJson Why I have not tested : json a class, was done in System.Text.Json https://github.com/ignatandrei/RSCG_Examples/issues/new?title=TeuJson&body=https://github.com/Terria-K/TeuJson -58) [Tinyhand]( https://github.com/archi-Doc/Tinyhand) , https://github.com/archi-Doc/Tinyhand +57) [Tinyhand]( https://github.com/archi-Doc/Tinyhand) , https://github.com/archi-Doc/Tinyhand Why I have not tested : tried, need documentation https://github.com/ignatandrei/RSCG_Examples/issues/new?title=Tinyhand&body=https://github.com/archi-Doc/Tinyhand -59) [TupleOverloadGenerator]( https://github.com/ProphetLamb/TupleOverloadGenerator) , https://github.com/ProphetLamb/TupleOverloadGenerator +58) [TupleOverloadGenerator]( https://github.com/ProphetLamb/TupleOverloadGenerator) , https://github.com/ProphetLamb/TupleOverloadGenerator Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=TupleOverloadGenerator&body=https://github.com/ProphetLamb/TupleOverloadGenerator -60) [TypealizR]( https://github.com/earloc/TypealizR) , https://github.com/earloc/TypealizR +59) [TypealizR]( https://github.com/earloc/TypealizR) , https://github.com/earloc/TypealizR Why I have not tested : depends on Microsoft.Extensions.Localization https://github.com/ignatandrei/RSCG_Examples/issues/new?title=TypealizR&body=https://github.com/earloc/TypealizR -61) [UnitTestBlazor]( https://github.com/bUnit-dev/bUnit) , https://github.com/bUnit-dev/bUnit +60) [UnitTestBlazor]( https://github.com/bUnit-dev/bUnit) , https://github.com/bUnit-dev/bUnit Why I have not tested : issue opened https://github.com/ignatandrei/RSCG_Examples/issues/new?title=UnitTestBlazor&body=https://github.com/bUnit-dev/bUnit -62) [ValueLink]( https://github.com/archi-Doc/ValueLink) , https://github.com/archi-Doc/ValueLink +61) [ValueLink]( https://github.com/archi-Doc/ValueLink) , https://github.com/archi-Doc/ValueLink Why I have not tested : too complicated https://github.com/ignatandrei/RSCG_Examples/issues/new?title=ValueLink&body=https://github.com/archi-Doc/ValueLink -63) [VisitorPatternGenerator]( https://github.com/hikarin522/VisitorPatternGenerator/) , https://github.com/hikarin522/VisitorPatternGenerator/ +62) [VisitorPatternGenerator]( https://github.com/hikarin522/VisitorPatternGenerator/) , https://github.com/hikarin522/VisitorPatternGenerator/ Why I have not tested : issue opened https://github.com/ignatandrei/RSCG_Examples/issues/new?title=VisitorPatternGenerator&body=https://github.com/hikarin522/VisitorPatternGenerator/ -64) [WrapperValueObject]( https://github.com/martinothamar/WrapperValueObject) , https://github.com/martinothamar/WrapperValueObject +63) [WrapperValueObject]( https://github.com/martinothamar/WrapperValueObject) , https://github.com/martinothamar/WrapperValueObject Why I have not tested : not maintained as in readme diff --git a/later.md b/later.md index c55efb561..c0665e407 100644 --- a/later.md +++ b/later.md @@ -1,6 +1,6 @@ # Just later -## Latest Update : 2025-08-16 => 16 August 2025 +## Latest Update : 2025-08-17 => 17 August 2025 @@ -86,55 +86,49 @@ Why I have not tested : later -14) [https://github.com/Tim-Maes/Facet]( https://github.com/Tim-Maes/Facet) , https://github.com/Tim-Maes/Facet +14) [Maui.BindableProperty.Generator]( https://github.com/rrmanzano/maui-bindableproperty-generator) , https://github.com/rrmanzano/maui-bindableproperty-generator Why I have not tested : later -15) [Maui.BindableProperty.Generator]( https://github.com/rrmanzano/maui-bindableproperty-generator) , https://github.com/rrmanzano/maui-bindableproperty-generator +15) [Minerals.AutoCQRS]( https://github.com/SzymonHalucha/Minerals.AutoCQRS) , https://github.com/SzymonHalucha/Minerals.AutoCQRS Why I have not tested : later -16) [Minerals.AutoCQRS]( https://github.com/SzymonHalucha/Minerals.AutoCQRS) , https://github.com/SzymonHalucha/Minerals.AutoCQRS +16) [Minerals.AutoDomain]( https://github.com/SzymonHalucha/Minerals.AutoDomain) , https://github.com/SzymonHalucha/Minerals.AutoDomain Why I have not tested : later -17) [Minerals.AutoDomain]( https://github.com/SzymonHalucha/Minerals.AutoDomain) , https://github.com/SzymonHalucha/Minerals.AutoDomain +17) [observable]( https://github.com/notanaverageman/Bindables) , https://github.com/notanaverageman/Bindables Why I have not tested : later -18) [observable]( https://github.com/notanaverageman/Bindables) , https://github.com/notanaverageman/Bindables +18) [RazorGen]( https://github.com/dartk/RazorGen) , https://github.com/dartk/RazorGen Why I have not tested : later -19) [RazorGen]( https://github.com/dartk/RazorGen) , https://github.com/dartk/RazorGen +19) [SourceCrafter.HttpServiceClientGenerator]( https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/) , https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ Why I have not tested : later -20) [SourceCrafter.HttpServiceClientGenerator]( https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/) , https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ +20) [ST.NSwag.ServerSourceGenerator]( https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator) , https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator Why I have not tested : later -21) [ST.NSwag.ServerSourceGenerator]( https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator) , https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator - -Why I have not tested : later - - - -22) [StrongInject]( https://github.com/YairHalberstadt/stronginject/) , https://github.com/YairHalberstadt/stronginject/ +21) [StrongInject]( https://github.com/YairHalberstadt/stronginject/) , https://github.com/YairHalberstadt/stronginject/ Why I have not tested : later diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv index 836a37092..a4a6679bd 100644 --- a/v2/Generator/all.csv +++ b/v2/Generator/all.csv @@ -227,3 +227,4 @@ Nr,Key,Source,Category 226,requiredenum, https://github.com/emptycoder/RequiredEnum,Enum 227,UtilityVerse.Copy, https://github.com/purkayasta/TheUtilityVerse,Clone 228,mvvmgen, https://github.com/thomasclaudiushuber/mvvmgen,Serializer +229,Facet, https://github.com/Tim-Maes/Facet/,Mapper diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json index 9386283e2..d81a4831d 100644 --- a/v2/RSCGExamplesData/GeneratorDataRec.json +++ b/v2/RSCGExamplesData/GeneratorDataRec.json @@ -1377,5 +1377,11 @@ "Category": 16, "dtStart": "2025-08-16T00:00:00", "show": true +}, +{ + "ID":"Facet", + "Category": 6, + "dtStart": "2025-08-17T00:00:00", + "show": true } ] \ No newline at end of file diff --git a/v2/RSCGExamplesData/NoExample.json b/v2/RSCGExamplesData/NoExample.json index f11012c52..a604cf96f 100644 --- a/v2/RSCGExamplesData/NoExample.json +++ b/v2/RSCGExamplesData/NoExample.json @@ -871,10 +871,6 @@ "name":"https://github.com/buchmiet/FastFsm", "why":"later" }, - {"ID":222, - "name":"https://github.com/Tim-Maes/Facet", - "why":"later" - }, { "ID":224, "name":"https://github.com/pstlnce/unflat", diff --git a/v2/book/examples/Facet.html b/v2/book/examples/Facet.html new file mode 100644 index 000000000..e114ede72 --- /dev/null +++ b/v2/book/examples/Facet.html @@ -0,0 +1,90 @@ + +

RSCG nr 229 : Facet

+ +

Info

+Nuget : https://www.nuget.org/packages/Facet/ + +

You can find more details at : https://github.com/Tim-Maes/Facet/

+ +

Author :Tim Maes

+ +

Source: https://github.com/Tim-Maes/Facet/

+ +

About

+ +Custom generation and mapper + +

+ How to use +

+

+ Add reference to the Facet in the csproj +

+ + +

This was for me the starting code

+ +
+ I have coded the file Program.cs +
+ +
+ +
+ I have coded the file Person.cs +
+ +
+ +
+ I have coded the file PersonDto.cs +
+ +
+

And here are the generated files

+ +
+ The file generated is PersonDTO.g.cs +
+ + +
+ The file generated is Extensions.Generated.cs +
+ + +
+ The file generated is LightweightObjectMapper.PreCodes.LightweightObjectMapperPreCodes.cs +
+ + +
+ The file generated is LightweightObjectMapper.PreCodes.PredefinedSpecialTypeMapping.cs +
+ + +
+ The file generated is LOMMapExtensions_mapperDemo_Person.g.cs +
+ + +
+ The file generated is PredefinedSpecialTypeMapping.Generated.cs +
+ + +

+ You can download the code and this page as pdf from + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet + +

+ + +

+ You can see the whole list at + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG + +

+ diff --git a/v2/book/list.html b/v2/book/list.html index a84c3529e..e53b409fd 100644 --- a/v2/book/list.html +++ b/v2/book/list.html @@ -17,7 +17,7 @@

-This is the list of 228 RSCG with examples => +This is the list of 229 RSCG with examples =>

@@ -938,6 +938,10 @@

+ + + +
228 mvvmgen
229Facet
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml index 6c458229c..aebb6f5db 100644 --- a/v2/book/pandocHTML.yaml +++ b/v2/book/pandocHTML.yaml @@ -242,6 +242,7 @@ input-files: - examples/requiredenum.html - examples/UtilityVerse.Copy.html - examples/mvvmgen.html +- examples/Facet.html # or you may use input-file: with a single value # defaults: diff --git a/v2/rscg_examples/Facet/description.json b/v2/rscg_examples/Facet/description.json new file mode 100644 index 000000000..67b58d412 --- /dev/null +++ b/v2/rscg_examples/Facet/description.json @@ -0,0 +1,22 @@ +{ + "generator":{ + "name":"Facet", + "nuget":[ + "https://www.nuget.org/packages/Facet/" + ], + "link":"https://github.com/Tim-Maes/Facet/", + "author":"Tim Maes", + "source":"https://github.com/Tim-Maes/Facet/" + }, + "data":{ + "goodFor":["Custom generation and mapper"], + "csprojDemo":"mapperDemo.csproj", + "csFiles":["Program.cs","Person.cs","PersonDto.cs"], + "excludeDirectoryGenerated":[""], + "includeAdditionalFiles":[""] + }, + "links":{ + "blog":"", + "video":"" + } +} \ No newline at end of file diff --git a/v2/rscg_examples/Facet/nuget.txt b/v2/rscg_examples/Facet/nuget.txt new file mode 100644 index 000000000..9c13c7e06 --- /dev/null +++ b/v2/rscg_examples/Facet/nuget.txt @@ -0,0 +1 @@ +Generate lean DTOs, slim views, or faceted projections of your models with a single attribute. \ No newline at end of file diff --git a/v2/rscg_examples/Facet/readme.txt b/v2/rscg_examples/Facet/readme.txt new file mode 100644 index 000000000..682ce98bf --- /dev/null +++ b/v2/rscg_examples/Facet/readme.txt @@ -0,0 +1,371 @@ +
+ Facet logo +
+ +
+"One part of a subject, situation, object that has many parts." +
+ +
+ +
+ +[![NuGet](https://img.shields.io/nuget/v/Facet.svg)](https://www.nuget.org/packages/Facet) +[![Downloads](https://img.shields.io/nuget/dt/Facet.svg)](https://www.nuget.org/packages/Facet) +[![GitHub](https://img.shields.io/github/license/Tim-Maes/Facet.svg)](https://github.com/Tim-Maes/Facet/blob/main/LICENSE.txt) +[![CI](https://github.com/Tim-Maes/Facet/actions/workflows/build.yml/badge.svg)](https://github.com/Tim-Maes/Facet/actions/workflows/build.yml) +[![CD](https://github.com/Tim-Maes/Facet/actions/workflows/release.yml/badge.svg)](https://github.com/Tim-Maes/Facet/actions/workflows/release.yml) + +
+ +--- + +**Facet** is a C# source generator that lets you define **lightweight projections** (DTOs, API models, etc.) directly from your domain models, without writing boilerplate. + +It generates partial classes, records, structs, or record structs with constructors, optional LINQ projections, and even supports custom mappings, all at compile time, with zero runtime cost. + +## :gem: What is Facetting? + +Facetting is the process of defining **focused views** of a larger model at compile time. + +Instead of manually writing separate DTOs, mappers, and projections, **Facet** allows you to declare what you want to keep, and generates everything else. + +You can think of it like **carving out a specific facet** of a gem: + +- The part you care about +- Leaving the rest behind. + +## :grey_question: Why Facetting? + +- Reduce duplication across DTOs, projections, and ViewModels +- Maintain strong typing with no runtime cost +- Stay DRY (Don't Repeat Yourself) without sacrificing performance +- Works seamlessly with LINQ providers like Entity Framework + +## :clipboard: Documentation + +- **[Documentation & Guides](docs/README.md)** +- [What is being generated?](docs/07_WhatIsBeingGenerated.md) + +## :star: Key Features + +- :white_check_mark: Generate classes, records, structs, or record structs from existing types +- :white_check_mark: Exclude fields/properties you don't want (create a Facetted view of your model) +- :white_check_mark: Include/redact public fields +- :white_check_mark: Auto-generate constructors for fast mapping +- :white_check_mark: LINQ projection expressions +- :white_check_mark: Full mapping support with custom mapping configurations +- :white_check_mark: Auto-generate complete CRUD DTO sets with `[GenerateDtos]` +- :white_check_mark: **Expression transformation and mapping utilities** for reusing business logic across entities and DTOs +- :white_check_mark: Preserves member and type XML documentation + +## :earth_americas: The Facet Ecosystem + +Facet is modular and consists of several NuGet packages: + +- **Facet**: The core source generator. Generates DTOs, projections, and mapping code. + +- **Facet.Extensions**: Provider-agnostic extension methods for mapping and projecting (works with any LINQ provider, no EF Core dependency). + +- **Facet.Mapping**: Advanced static mapping configuration support with async capabilities and dependency injection for complex mapping scenarios. + +- **Facet.Mapping.Expressions**: Expression tree transformation utilities for transforming predicates, selectors, and business logic between source entities and their Facet projections. + +- **Facet.Extensions.EFCore**: Async extension methods for Entity Framework Core (requires EF Core 6+). + +## :rocket: Quick start + +### Install the NuGet Package + +``` +dotnet add package Facet +``` + +For LINQ helpers: +``` +dotnet add package Facet.Extensions +``` + +For EF Core support: +``` +dotnet add package Facet.Extensions.EFCore +``` + +For expression transformation utilities: +``` +dotnet add package Facet.Mapping.Expressions +``` + +### Basic Projection +```csharp +[Facet(typeof(User))] +public partial class UserFacet { } + +// Auto-generates constructor, properties, and LINQ projection +var user = user.ToFacet(); +var user = user.ToFacet(); //Much faster + +var users = users.SelectFacets(); +var users = users.SelectFacets(); //Much faster +``` + +### Property Exclusion & Field Inclusion +```csharp +// Exclude sensitive properties +string[] excludeFields = { "Password", "Email" }; + +[Facet(typeof(User), exclude: excludeFields)] +public partial class UserWithoutEmail { } + +// Include public fields +[Facet(typeof(Entity), IncludeFields = true)] +public partial class EntityDto { } +``` + +### Different Type Kinds +```csharp +// Generate as record (immutable by default) +[Facet(typeof(Product))] +public partial record ProductDto; + +// Generate as struct (value type) +[Facet(typeof(Point))] +public partial struct PointDto; + +// Generate as record struct (immutable value type) +[Facet(typeof(Coordinates))] +public partial record struct CoordinatesDto; // Preserves required/init-only +``` + +### Custom Sync Mapping +```csharp +public class UserMapper : IFacetMapConfiguration +{ + public static void Map(User source, UserDto target) + { + target.FullName = $"{source.FirstName} {source.LastName}"; + target.Age = CalculateAge(source.DateOfBirth); + } +} + +[Facet(typeof(User), Configuration = typeof(UserMapper))] +public partial class UserDto +{ + public string FullName { get; set; } + public int Age { get; set; } +} +``` + +### Async Mapping for I/O Operations +```csharp +public class UserAsyncMapper : IFacetMapConfigurationAsync +{ + public static async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default) + { + // Async database lookup + target.ProfilePicture = await GetProfilePictureAsync(source.Id, cancellationToken); + + // Async API call + target.ReputationScore = await CalculateReputationAsync(source.Email, cancellationToken); + } +} + +// Usage +var userDto = await user.ToFacetAsync(); +var userDtos = await users.ToFacetsParallelAsync(); +``` + +### Async Mapping with Dependency Injection +```csharp +public class UserAsyncMapperWithDI : IFacetMapConfigurationAsyncInstance +{ + private readonly IProfilePictureService _profileService; + private readonly IReputationService _reputationService; + + public UserAsyncMapperWithDI(IProfilePictureService profileService, IReputationService reputationService) + { + _profileService = profileService; + _reputationService = reputationService; + } + + public async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default) + { + // Use injected services + target.ProfilePicture = await _profileService.GetProfilePictureAsync(source.Id, cancellationToken); + target.ReputationScore = await _reputationService.CalculateReputationAsync(source.Email, cancellationToken); + } +} + +// Usage with DI +var mapper = new UserAsyncMapperWithDI(profileService, reputationService); +var userDto = await user.ToFacetAsync(mapper); +var userDtos = await users.ToFacetsParallelAsync(mapper); +``` + +### EF Core Integration + +#### Forward Mapping (Entity -> Facet) +```csharp +// Async projection directly in EF Core queries +var userDtos = await dbContext.Users + .Where(u => u.IsActive) + .ToFacetsAsync(); + +// LINQ projection for complex queries +var results = await dbContext.Products + .Where(p => p.IsAvailable) + .SelectFacet() + .OrderBy(dto => dto.Name) + .ToListAsync(); +``` + +#### Reverse Mapping (Facet -> Entity) +```csharp +[Facet(typeof(User)] +public partial class UpdateUserDto { } + +[HttpPut("{id}")] +public async Task UpdateUser(int id, UpdateUserDto dto) +{ + var user = await context.Users.FindAsync(id); + if (user == null) return NotFound(); + + // Only updates properties that mutated + user.UpdateFromFacet(dto, context); + + await context.SaveChangesAsync(); + return NoContent(); +} + +// With change tracking for auditing +var result = user.UpdateFromFacetWithChanges(dto, context); +if (result.HasChanges) +{ + logger.LogInformation("User {UserId} updated. Changed: {Properties}", + user.Id, string.Join(", ", result.ChangedProperties)); +} +``` + +### Automatic CRUD DTO Generation + +Generate standard Create, Update, Response, Query, and Upsert DTOs automatically: + +```csharp +// Generate all standard CRUD DTOs +[GenerateDtos(Types = DtoTypes.All, OutputType = OutputType.Record)] +public class User +{ + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public DateTime CreatedAt { get; set; } +} + +// Auto-generates: +// - CreateUserRequest (excludes Id) +// - UpdateUserRequest (includes Id) +// - UserResponse (includes all) +// - UserQuery (all properties nullable) +// - UpsertUserRequest (includes Id, for create/update operations) +``` + +#### +Entities with Smart Exclusions +```csharp +[GenerateAuditableDtos( + Types = DtoTypes.Create | DtoTypes.Update | DtoTypes.Response, + OutputType = OutputType.Record, + ExcludeProperties = new[] { "Password" })] +public class Product +{ + public int Id { get; set; } + public string Name { get; set; } + public string Password { get; set; } // Excluded + public DateTime CreatedAt { get; set; } // Auto-excluded (audit) + public string CreatedBy { get; set; } // Auto-excluded (audit) +} + +// Auto-excludes audit fields: CreatedAt, UpdatedAt, CreatedBy, UpdatedBy +``` + +#### Multiple Configurations for Fine-Grained Control +```csharp +// Different exclusions for different DTO types +[GenerateDtos(Types = DtoTypes.Response, ExcludeProperties = new[] { "Password", "InternalNotes" })] +[GenerateDtos(Types = DtoTypes.Upsert, ExcludeProperties = new[] { "Password" })] +public class Schedule +{ + public int Id { get; set; } + public string Name { get; set; } + public string Password { get; set; } // Excluded from both + public string InternalNotes { get; set; } // Only excluded from Response +} + +// Generates: +// - ScheduleResponse (excludes Password, InternalNotes) +// - UpsertScheduleRequest (excludes Password, includes InternalNotes) +``` + +#### Perfect for RESTful APIs +```csharp +[HttpPost] +public async Task> CreateSchedule(CreateScheduleRequest request) +{ + var schedule = new Schedule + { + Name = request.Name, + // Map other properties;;; + }; + + context.Schedules.Add(schedule); + await context.SaveChangesAsync(); + return schedule.ToFacet(); +} + +[HttpPut("{id}")] +public async Task> UpsertSchedule(int id, UpsertScheduleRequest body) +{ + var schedule = context.GetScheduleById(id); + if (schedule == null) return NotFound(); + + // Ensure the body ID matches the route ID + body = body with { Id = id }; + + schedule.UpdateFromFacet(body, context); + await context.SaveChangesAsync(); + return schedule.ToFacet(); +} +``` + +## :chart_with_upwards_trend: Performance Benchmarks + +Facet delivers competitive performance across different mapping scenarios. Here's how it compares to popular alternatives: + +### Single Mapping + +| Library | Mean Time | Memory Allocated | Performance vs Facet | +|----------|-----------|------------------|---------------------| +| **Facet** | 15.93 ns | 136 B | **Baseline** | +| Mapperly | 15.09 ns | 128 B | 5% faster, 6% less memory | +| Mapster | 21.90 ns | 128 B | 38% slower, 6% less memory | + +### Collection Mapping (10 items) + +| Library | Mean Time | Memory Allocated | Performance vs Facet | +|----------|-----------|------------------|---------------------| +| Mapster | 192.55 ns | 1,416 B | **10% faster, 10% less memory** | +| **Facet** | 207.32 ns | 1,568 B | **Baseline** | +| Mapperly | 222.50 ns | 1,552 B | 7% slower, 1% less memory | + +For this benchmark we used the `` methods. + +**Insights:** +> - **Single mapping**: All three libraries perform similarly with sub-nanosecond differences +> - **Collection mapping**: Mapster has a slight edge for bulk operations, while Facet and Mapperly are very close +> - **Memory efficiency**: All libraries are within ~10% of each other for memory allocation +> - **Compile-time generation**: Both Facet and Mapperly benefit from zero-runtime-cost source generation + diff --git a/v2/rscg_examples/Facet/src/.tours/Facet.tour b/v2/rscg_examples/Facet/src/.tours/Facet.tour new file mode 100644 index 000000000..e36f8bf0e --- /dev/null +++ b/v2/rscg_examples/Facet/src/.tours/Facet.tour @@ -0,0 +1,72 @@ + +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "Facet", + "steps": + [ + { + "file": "mapperDemo/mapperDemo.csproj", + "description": "First, we add Nuget [Facet](https://www.nuget.org/packages/Facet/) in csproj ", + "pattern": "Facet" + } + + ,{ + "file": "mapperDemo/PersonDTO.cs", + "description": "File PersonDTO.cs ", + "pattern": "this is the code" + } + + ,{ + "file": "mapperDemo/Person.cs", + "description": "File Person.cs ", + "pattern": "this is the code" + } + + ,{ + "file": "mapperDemo/Program.cs", + "description": "File Program.cs \r\n>> dotnet run --project mapperDemo/mapperDemo.csproj ", + "pattern": "this is the code" + } + + + ,{ + "file": "mapperDemo/obj/GX/LightweightObjectMapper/LightweightObjectMapper.LightweightObjectMapperSourceGenerator/PredefinedSpecialTypeMapping.Generated.cs", + "description": "Generated File 6 from 6 : PredefinedSpecialTypeMapping.Generated.cs ", + "line": 1 + } + + ,{ + "file": "mapperDemo/obj/GX/LightweightObjectMapper/LightweightObjectMapper.LightweightObjectMapperSourceGenerator/LOMMapExtensions_mapperDemo_Person.g.cs", + "description": "Generated File 5 from 6 : LOMMapExtensions_mapperDemo_Person.g.cs ", + "line": 1 + } + + ,{ + "file": "mapperDemo/obj/GX/LightweightObjectMapper/LightweightObjectMapper.LightweightObjectMapperSourceGenerator/LightweightObjectMapper.PreCodes.PredefinedSpecialTypeMapping.cs", + "description": "Generated File 4 from 6 : LightweightObjectMapper.PreCodes.PredefinedSpecialTypeMapping.cs ", + "line": 1 + } + + ,{ + "file": "mapperDemo/obj/GX/LightweightObjectMapper/LightweightObjectMapper.LightweightObjectMapperSourceGenerator/LightweightObjectMapper.PreCodes.LightweightObjectMapperPreCodes.cs", + "description": "Generated File 3 from 6 : LightweightObjectMapper.PreCodes.LightweightObjectMapperPreCodes.cs ", + "line": 1 + } + + ,{ + "file": "mapperDemo/obj/GX/LightweightObjectMapper/LightweightObjectMapper.LightweightObjectMapperSourceGenerator/Extensions.Generated.cs", + "description": "Generated File 2 from 6 : Extensions.Generated.cs ", + "line": 1 + } + + ,{ + "file": "mapperDemo/obj/GX/Facet/Facet.Generators.FacetGenerator/PersonDTO.g.cs", + "description": "Generated File 1 from 6 : PersonDTO.g.cs ", + "line": 1 + } + + ], + + "ref": "main" + +} \ No newline at end of file diff --git a/v2/rscg_examples/Facet/src/mapperDemo.sln b/v2/rscg_examples/Facet/src/mapperDemo.sln new file mode 100644 index 000000000..593a243fb --- /dev/null +++ b/v2/rscg_examples/Facet/src/mapperDemo.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33815.320 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mapperDemo", "mapperDemo\mapperDemo.csproj", "{184A6543-E3EE-4397-B830-A165D7978DB3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {184A6543-E3EE-4397-B830-A165D7978DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {184A6543-E3EE-4397-B830-A165D7978DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {184A6543-E3EE-4397-B830-A165D7978DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {184A6543-E3EE-4397-B830-A165D7978DB3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D86ADE0F-0F05-4248-9962-D53AAADDD0D6} + EndGlobalSection +EndGlobal diff --git a/v2/rscg_examples/Facet/src/mapperDemo/Person.cs b/v2/rscg_examples/Facet/src/mapperDemo/Person.cs new file mode 100644 index 000000000..c1c18c040 --- /dev/null +++ b/v2/rscg_examples/Facet/src/mapperDemo/Person.cs @@ -0,0 +1,8 @@ + +public partial class Person +{ + public int ID { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } +} + diff --git a/v2/rscg_examples/Facet/src/mapperDemo/PersonDTO.cs b/v2/rscg_examples/Facet/src/mapperDemo/PersonDTO.cs new file mode 100644 index 000000000..ded406a29 --- /dev/null +++ b/v2/rscg_examples/Facet/src/mapperDemo/PersonDTO.cs @@ -0,0 +1,13 @@ + +using Facet; + +namespace mapperDemo; +[Facet(typeof(Person), Kind = FacetKind.Struct)] +public partial struct PersonDTO +{ public string FullName { + get + { + return FirstName + " " + LastName; + } + } +} diff --git a/v2/rscg_examples/Facet/src/mapperDemo/Program.cs b/v2/rscg_examples/Facet/src/mapperDemo/Program.cs new file mode 100644 index 000000000..27cdc83cb --- /dev/null +++ b/v2/rscg_examples/Facet/src/mapperDemo/Program.cs @@ -0,0 +1,6 @@ +using mapperDemo; +var p=new Person(); +p.FirstName = "Andrei"; +p.LastName = "Ignat"; +PersonDTO dto= new(p); +Console.WriteLine(dto.FullName); diff --git a/v2/rscg_examples/Facet/src/mapperDemo/mapperDemo.csproj b/v2/rscg_examples/Facet/src/mapperDemo/mapperDemo.csproj new file mode 100644 index 000000000..c86ab252b --- /dev/null +++ b/v2/rscg_examples/Facet/src/mapperDemo/mapperDemo.csproj @@ -0,0 +1,22 @@ + + + + Exe + net8.0 + enable + enable + + + + true + $(BaseIntermediateOutputPath)\GX + + + + + + + + + + diff --git a/v2/rscg_examples/Facet/video.json b/v2/rscg_examples/Facet/video.json new file mode 100644 index 000000000..10645af89 --- /dev/null +++ b/v2/rscg_examples/Facet/video.json @@ -0,0 +1,39 @@ +{ + "scriptName": "Facet", + "steps": +[ + {"typeStep":"exec","arg":"clipchamp.exe launch"}, + {"typeStep":"text","arg": "Welcome to Roslyn Examples"}, + {"typeStep":"text","arg":"If you want to see more examples , see List Of RSCG"}, + {"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG"}, + {"typeStep":"text","arg": "My name is Andrei Ignat and I am deeply fond of Roslyn Source Code Generator. "}, + +{"typeStep":"text","arg": "Today I will present Facet . Custom generation and mapper ."}, +{"typeStep":"browser","arg":"https://www.nuget.org/packages/Facet/"}, +{"typeStep":"text","arg": "The whole example is here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet"}, +{"typeStep":"text","arg": "You can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet#download-example-net--c-"}, +{"typeStep":"text","arg":"Here is the code downloaded "}, +{"typeStep":"exec","arg":"explorer.exe /select,D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Facet\\src\\mapperDemo.sln"}, +{"typeStep":"text","arg": "So , let's start the project with Visual Studio Code "}, +{"typeStep":"stepvscode","arg": "-n D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Facet\\src"}, + +{"typeStep":"text","arg": "To use it ,you will put the Nuget Facet into the csproj "}, + +{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\Facet\\src\\mapperDemo\\mapperDemo.csproj"}, + +{"typeStep":"text","arg": "And now I will show you an example of using Facet"}, + +{"typeStep":"hide","arg": "now execute the tour in VSCode"}, +{"typeStep":"tour", "arg": "src/.tours/"}, +{"typeStep":"text","arg":" And I will execute the project"}, +{"typeStep":"showproj", "arg":"mapperDemo.csproj"}, +{"typeStep":"text","arg":" This concludes the project"}, +{"typeStep":"waitseconds","arg":"30"}, +{"typeStep":"text","arg": "Remember, you can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet#download-example-net--c-", +SpeakTest=" "}, +{"typeStep":"waitseconds","arg":"30"}, +] +} diff --git a/v2/rscg_examples_site/docs/Categories/Mapper.md b/v2/rscg_examples_site/docs/Categories/Mapper.md index eb6c6f5fb..19e584e77 100644 --- a/v2/rscg_examples_site/docs/Categories/Mapper.md +++ b/v2/rscg_examples_site/docs/Categories/Mapper.md @@ -1,6 +1,6 @@

Mapper

-Number RSCG: 8 +Number RSCG: 9 1 [AutoDTO](/docs/AutoDTO) @@ -8,13 +8,15 @@ Number RSCG: 8 3 [DynamicsMapper](/docs/DynamicsMapper) - 4 [LightweightObjectMapper](/docs/LightweightObjectMapper) + 4 [Facet](/docs/Facet) - 5 [MagicMap](/docs/MagicMap) + 5 [LightweightObjectMapper](/docs/LightweightObjectMapper) - 6 [mapperly](/docs/mapperly) + 6 [MagicMap](/docs/MagicMap) - 7 [MapTo](/docs/MapTo) + 7 [mapperly](/docs/mapperly) - 8 [NextGenMapper](/docs/NextGenMapper) + 8 [MapTo](/docs/MapTo) + + 9 [NextGenMapper](/docs/NextGenMapper) \ No newline at end of file diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveMapper.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveMapper.mdx index e93c88ec4..ceb5f3210 100644 --- a/v2/rscg_examples_site/docs/Categories/_PrimitiveMapper.mdx +++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveMapper.mdx @@ -6,15 +6,17 @@ 3 [DynamicsMapper](/docs/DynamicsMapper) - 4 [LightweightObjectMapper](/docs/LightweightObjectMapper) + 4 [Facet](/docs/Facet) - 5 [MagicMap](/docs/MagicMap) + 5 [LightweightObjectMapper](/docs/LightweightObjectMapper) - 6 [mapperly](/docs/mapperly) + 6 [MagicMap](/docs/MagicMap) - 7 [MapTo](/docs/MapTo) + 7 [mapperly](/docs/mapperly) - 8 [NextGenMapper](/docs/NextGenMapper) + 8 [MapTo](/docs/MapTo) + + 9 [NextGenMapper](/docs/NextGenMapper) ### See category diff --git a/v2/rscg_examples_site/docs/NoExamples.md b/v2/rscg_examples_site/docs/NoExamples.md index eb4cd188f..f9a7a0239 100644 --- a/v2/rscg_examples_site/docs/NoExamples.md +++ b/v2/rscg_examples_site/docs/NoExamples.md @@ -354,359 +354,355 @@ Why I have not put example: later Why I have not put example: too complicated -88)https://github.com/Tim-Maes/Facet https://github.com/Tim-Maes/Facet - -Why I have not put example: later - -89)https://github.com/wieslawsoltes/ReactiveGenerator https://github.com/wieslawsoltes/ReactiveGenerator +88)https://github.com/wieslawsoltes/ReactiveGenerator https://github.com/wieslawsoltes/ReactiveGenerator Why I have not put example: too complicated -90)HubClientProxyGenerator https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client.SourceGenerator +89)HubClientProxyGenerator https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client.SourceGenerator Why I have not put example: not having nuget, but having IIncrementalGenerator -91)Imp.NET https://github.com/DouglasDwyer/Imp.NET +90)Imp.NET https://github.com/DouglasDwyer/Imp.NET Why I have not put example: old ISourceGenerator -92)Intellenum https://github.com/SteveDunn/Intellenum +91)Intellenum https://github.com/SteveDunn/Intellenum Why I have not put example: not understand how to use -93)InterfaceGenerator https://github.com/daver32/InterfaceGenerator +92)InterfaceGenerator https://github.com/daver32/InterfaceGenerator Why I have not put example: old ISourceGenerator -94)IoTHubClientGenerator https://github.com/alonf/IoTHubClientGenerator +93)IoTHubClientGenerator https://github.com/alonf/IoTHubClientGenerator Why I have not put example: old ISourceGenerator -95)JsonByExampleGenerator https://github.com/hermanussen/JsonByExampleGenerator +94)JsonByExampleGenerator https://github.com/hermanussen/JsonByExampleGenerator Why I have not put example: old ISourceGenerator -96)JsonDeserializeResourceSourceGenerator https://github.com/musictopia2/JsonDeserializeResourceSourceGenerator +95)JsonDeserializeResourceSourceGenerator https://github.com/musictopia2/JsonDeserializeResourceSourceGenerator Why I have not put example: no readme -97)JsonMergePatch https://github.com/ladeak/JsonMergePatch +96)JsonMergePatch https://github.com/ladeak/JsonMergePatch Why I have not put example: old ISourceGenerator -98)JsonSerializerContextGenerator https://github.com/musictopia2/JsonSerializerContextGenerator +97)JsonSerializerContextGenerator https://github.com/musictopia2/JsonSerializerContextGenerator Why I have not put example: no readme -99)JsonSourceGenerator https://github.com/Pilchie/JsonSourceGenerator +98)JsonSourceGenerator https://github.com/Pilchie/JsonSourceGenerator Why I have not put example: not having nuget, but having IIncrementalGenerator -100)JsonSrcGen https://github.com/trampster/JsonSrcGen +99)JsonSrcGen https://github.com/trampster/JsonSrcGen Why I have not put example: old ISourceGenerator -101)kli.Localize https://github.com/kl1mm/localize +100)kli.Localize https://github.com/kl1mm/localize Why I have not put example: old ISourceGenerator -102)laker https://github.com/Lakerfield/Lakerfield.Rpc +101)laker https://github.com/Lakerfield/Lakerfield.Rpc Why I have not put example: too complicated -103)lambdajection https://github.com/cythral/lambdajection +102)lambdajection https://github.com/cythral/lambdajection Why I have not put example: old ISourceGenerator -104)Lazysh https://github.com/B1Z0N/LazyshGen +103)Lazysh https://github.com/B1Z0N/LazyshGen Why I have not put example: old ISourceGenerator -105)LoggingDecoratorGenerator https://github.com/DavidFineboym/LoggingDecoratorGenerator +104)LoggingDecoratorGenerator https://github.com/DavidFineboym/LoggingDecoratorGenerator Why I have not put example: Microsoft have done same feature -106)lucide-blazor https://github.com/brecht-vde/lucide-blazor/ +105)lucide-blazor https://github.com/brecht-vde/lucide-blazor/ Why I have not put example: issue opened -107)ManagedDotnetProfiler https://github.com/kevingosse/ManagedDotnetProfiler +106)ManagedDotnetProfiler https://github.com/kevingosse/ManagedDotnetProfiler Why I have not put example: too complicated -108)MapDataReader https://github.com/jitbit/MapDataReader +107)MapDataReader https://github.com/jitbit/MapDataReader Why I have not put example: old ISourceGenerator -109)MappingCloningExtensions https://github.com/musictopia2/MappingCloningExtensions +108)MappingCloningExtensions https://github.com/musictopia2/MappingCloningExtensions Why I have not put example: no readme -110)Maui.BindableProperty.Generator https://github.com/rrmanzano/maui-bindableproperty-generator +109)Maui.BindableProperty.Generator https://github.com/rrmanzano/maui-bindableproperty-generator Why I have not put example: later -111)MediatR https://github.com/Burgyn/MMLib.MediatR.Generators +110)MediatR https://github.com/Burgyn/MMLib.MediatR.Generators Why I have not put example: old ISourceGenerator -112)MemberAccessGenerator https://github.com/ufcpp/MemberAccessGenerator +111)MemberAccessGenerator https://github.com/ufcpp/MemberAccessGenerator Why I have not put example: old ISourceGenerator -113)MemoizeSourceGenerator https://github.com/Zoxive/MemoizeSourceGenerator +112)MemoizeSourceGenerator https://github.com/Zoxive/MemoizeSourceGenerator Why I have not put example: old ISourceGenerator -114)Minerals.AutoCQRS https://github.com/SzymonHalucha/Minerals.AutoCQRS +113)Minerals.AutoCQRS https://github.com/SzymonHalucha/Minerals.AutoCQRS Why I have not put example: later -115)Minerals.AutoDomain https://github.com/SzymonHalucha/Minerals.AutoDomain +114)Minerals.AutoDomain https://github.com/SzymonHalucha/Minerals.AutoDomain Why I have not put example: later -116)MiniRazor https://github.com/Tyrrrz/MiniRazor/ +115)MiniRazor https://github.com/Tyrrrz/MiniRazor/ Why I have not put example: archived -117)MockableStaticGenerator https://github.com/HamedFathi/MockableStaticGenerator +116)MockableStaticGenerator https://github.com/HamedFathi/MockableStaticGenerator Why I have not put example: old ISourceGenerator -118)MockGen https://github.com/thomas-girotto/MockGen +117)MockGen https://github.com/thomas-girotto/MockGen Why I have not put example: old ISourceGenerator -119)MockSourceGenerator https://github.com/hermanussen/MockSourceGenerator +118)MockSourceGenerator https://github.com/hermanussen/MockSourceGenerator Why I have not put example: old ISourceGenerator -120)MrMeeseeks.DIE https://github.com/Yeah69/MrMeeseeks.DIE +119)MrMeeseeks.DIE https://github.com/Yeah69/MrMeeseeks.DIE Why I have not put example: old ISourceGenerator -121)MrMeeseeks.ResXToViewModelGenerator https://github.com/Yeah69/MrMeeseeks.ResXToViewModelGenerator +120)MrMeeseeks.ResXToViewModelGenerator https://github.com/Yeah69/MrMeeseeks.ResXToViewModelGenerator Why I have not put example: old ISourceGenerator -122)MrMeeseeks.StaticDelegateGenerator https://github.com/Yeah69/MrMeeseeks.StaticDelegateGenerator +121)MrMeeseeks.StaticDelegateGenerator https://github.com/Yeah69/MrMeeseeks.StaticDelegateGenerator Why I have not put example: old ISourceGenerator -123)MrMeeseeks.Visitor https://github.com/Yeah69/MrMeeseeks.Visitor +122)MrMeeseeks.Visitor https://github.com/Yeah69/MrMeeseeks.Visitor Why I have not put example: old ISourceGenerator -124)Neon.Roslyn https://www.nuget.org/packages/Neon.Roslyn +123)Neon.Roslyn https://www.nuget.org/packages/Neon.Roslyn Why I have not put example: old ISourceGenerator -125)net_automatic_interface https://github.com/codecentric/net_automatic_interface +124)net_automatic_interface https://github.com/codecentric/net_automatic_interface Why I have not put example: old ISourceGenerator -126)NSourceGenerators https://github.com/NeVeSpl/NSourceGenerators/ +125)NSourceGenerators https://github.com/NeVeSpl/NSourceGenerators/ Why I have not put example: old ISourceGenerator -127)observable https://github.com/notanaverageman/Bindables +126)observable https://github.com/notanaverageman/Bindables Why I have not put example: later -128)Pipelines https://github.com/DumplingsDevs/Pipelines/ +127)Pipelines https://github.com/DumplingsDevs/Pipelines/ Why I have not put example: old ISourceGenerator -129)Plastic https://github.com/sang-hyeon/Plastic +128)Plastic https://github.com/sang-hyeon/Plastic Why I have not put example: old ISourceGenerator -130)PolySharp https://github.com/Sergio0694/PolySharp +129)PolySharp https://github.com/Sergio0694/PolySharp Why I have not put example: too complicated -131)PrimaryConstructor https://github.com/chaowlert/PrimaryConstructor +130)PrimaryConstructor https://github.com/chaowlert/PrimaryConstructor Why I have not put example: old ISourceGenerator -132)PrimitiveStaticDataGenerator https://github.com/iiweis/PrimitiveStaticDataGenerator +131)PrimitiveStaticDataGenerator https://github.com/iiweis/PrimitiveStaticDataGenerator Why I have not put example: old ISourceGenerator -133)PrintMembersGenerator https://github.com/Youssef1313/PrintMembersGenerator +132)PrintMembersGenerator https://github.com/Youssef1313/PrintMembersGenerator Why I have not put example: old ISourceGenerator -134)ProxyInterfaceGenerator https://github.com/StefH/ProxyInterfaceSourceGenerator +133)ProxyInterfaceGenerator https://github.com/StefH/ProxyInterfaceSourceGenerator Why I have not put example: old ISourceGenerator -135)PureHDF https://github.com/Apollo3zehn/PureHDF +134)PureHDF https://github.com/Apollo3zehn/PureHDF Why I have not put example: old ISourceGenerator -136)RazorGen https://github.com/dartk/RazorGen +135)RazorGen https://github.com/dartk/RazorGen Why I have not put example: later -137)RazorPageRouteGenerator https://github.com/surgicalcoder/RazorPageRouteGenerator +136)RazorPageRouteGenerator https://github.com/surgicalcoder/RazorPageRouteGenerator Why I have not put example: old ISourceGenerator -138)ReForge.Union https://github.com/nalcorso/ReForge.Union +137)ReForge.Union https://github.com/nalcorso/ReForge.Union Why I have not put example: not having nuget, but having IIncrementalGenerator -139)RoslynWeave https://github.com/Jishun/RoslynWeave +138)RoslynWeave https://github.com/Jishun/RoslynWeave Why I have not put example: old ISourceGenerator -140)ScenarioTests https://github.com/koenbeuk/ScenarioTests +139)ScenarioTests https://github.com/koenbeuk/ScenarioTests Why I have not put example: old ISourceGenerator -141)SerdeDn https://github.com/serdedotnet/serde +140)SerdeDn https://github.com/serdedotnet/serde Why I have not put example: serializer. Done by MSFT with System.Text.Json -142)SmallSharp https://github.com/devlooped/SmallSharp +141)SmallSharp https://github.com/devlooped/SmallSharp Why I have not put example: old ISourceGenerator -143)SmartAnnotations https://github.com/fiseni/SmartAnnotations +142)SmartAnnotations https://github.com/fiseni/SmartAnnotations Why I have not put example: old ISourceGenerator -144)SogePoco https://github.com/d-p-y/SogePoco +143)SogePoco https://github.com/d-p-y/SogePoco Why I have not put example: too complicated -145)SourceApi https://github.com/alekshura/SourceApi +144)SourceApi https://github.com/alekshura/SourceApi Why I have not put example: old ISourceGenerator -146)SourceConfig https://github.com/alekshura/SourceConfig +145)SourceConfig https://github.com/alekshura/SourceConfig Why I have not put example: old ISourceGenerator -147)SourceCrafter.HttpServiceClientGenerator https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ +146)SourceCrafter.HttpServiceClientGenerator https://github.com/pedro-gilmora/SourceCrafter.HttpServiceClientGenerator/ Why I have not put example: later -148)SourceGeneratorQuery https://github.com/roeibajayo/SourceGeneratorQuery +147)SourceGeneratorQuery https://github.com/roeibajayo/SourceGeneratorQuery Why I have not put example: old ISourceGenerator -149)SourceInject https://github.com/giggio/sourceinject/ +148)SourceInject https://github.com/giggio/sourceinject/ Why I have not put example: old ISourceGenerator -150)SourceMapper https://github.com/alekshura/SourceMapper +149)SourceMapper https://github.com/alekshura/SourceMapper Why I have not put example: old ISourceGenerator -151)SourceMapper https://github.com/paiden/SourceMapper/ +150)SourceMapper https://github.com/paiden/SourceMapper/ Why I have not put example: old ISourceGenerator -152)SqlMarshal https://github.com/kant2002/SqlMarshal +151)SqlMarshal https://github.com/kant2002/SqlMarshal Why I have not put example: old ISourceGenerator -153)ST.NSwag.ServerSourceGenerator https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator +152)ST.NSwag.ServerSourceGenerator https://github.com/s-tarasov/ST.NSwag.ServerSourceGenerator Why I have not put example: later -154)StackXML https://github.com/ZingBallyhoo/StackXML +153)StackXML https://github.com/ZingBallyhoo/StackXML Why I have not put example: old ISourceGenerator -155)StaticProxyGenerator https://github.com/robertturner/StaticProxyGenerator +154)StaticProxyGenerator https://github.com/robertturner/StaticProxyGenerator Why I have not put example: old ISourceGenerator -156)StrongInject https://github.com/YairHalberstadt/stronginject/ +155)StrongInject https://github.com/YairHalberstadt/stronginject/ Why I have not put example: later -157)StronglyTypedEmbeddedResources https://github.com/surgicalcoder/StronglyTypedEmbeddedResources +156)StronglyTypedEmbeddedResources https://github.com/surgicalcoder/StronglyTypedEmbeddedResources Why I have not put example: old ISourceGenerator -158)StructPacker https://github.com/RudolfKurka/StructPacker +157)StructPacker https://github.com/RudolfKurka/StructPacker Why I have not put example: old ISourceGenerator -159)Svg https://github.com/wieslawsoltes/Svg.Skia +158)Svg https://github.com/wieslawsoltes/Svg.Skia Why I have not put example: old ISourceGenerator -160)tecli https://github.com/tyevco/TeCLI +159)tecli https://github.com/tyevco/TeCLI Why I have not put example: old ISourceGenerator -161)TeuJson https://github.com/Terria-K/TeuJson +160)TeuJson https://github.com/Terria-K/TeuJson Why I have not put example: json a class, was done in System.Text.Json -162)Thunderboltloc https://github.com/AlyElhaddad/ThunderboltIoc +161)Thunderboltloc https://github.com/AlyElhaddad/ThunderboltIoc Why I have not put example: old ISourceGenerator -163)Tinyhand https://github.com/archi-Doc/Tinyhand +162)Tinyhand https://github.com/archi-Doc/Tinyhand Why I have not put example: tried, need documentation -164)ToString https://github.com/Burgyn/MMLib.ToString +163)ToString https://github.com/Burgyn/MMLib.ToString Why I have not put example: old ISourceGenerator -165)Transplator https://github.com/atifaziz/Transplator +164)Transplator https://github.com/atifaziz/Transplator Why I have not put example: old ISourceGenerator -166)TupleOverloadGenerator https://github.com/ProphetLamb/TupleOverloadGenerator +165)TupleOverloadGenerator https://github.com/ProphetLamb/TupleOverloadGenerator Why I have not put example: too complicated -167)TxtToListGenerator https://github.com/musictopia2/TxtToListGenerator +166)TxtToListGenerator https://github.com/musictopia2/TxtToListGenerator Why I have not put example: no readme -168)TypealizR https://github.com/earloc/TypealizR +167)TypealizR https://github.com/earloc/TypealizR Why I have not put example: depends on Microsoft.Extensions.Localization -169)UnitTestBlazor https://github.com/bUnit-dev/bUnit +168)UnitTestBlazor https://github.com/bUnit-dev/bUnit Why I have not put example: issue opened -170)ValueChangedGenerator https://github.com/ufcpp/ValueChangedGenerator +169)ValueChangedGenerator https://github.com/ufcpp/ValueChangedGenerator Why I have not put example: old ISourceGenerator -171)ValueLink https://github.com/archi-Doc/ValueLink +170)ValueLink https://github.com/archi-Doc/ValueLink Why I have not put example: too complicated -172)ValueObjectGenerator https://github.com/RyotaMurohoshi/ValueObjectGenerator +171)ValueObjectGenerator https://github.com/RyotaMurohoshi/ValueObjectGenerator Why I have not put example: old ISourceGenerator -173)VisitorPatternGenerator https://github.com/hikarin522/VisitorPatternGenerator/ +172)VisitorPatternGenerator https://github.com/hikarin522/VisitorPatternGenerator/ Why I have not put example: issue opened -174)Visor https://github.com/Tinkoff/Visor +173)Visor https://github.com/Tinkoff/Visor Why I have not put example: archived -175)WrapperValueObject https://github.com/martinothamar/WrapperValueObject +174)WrapperValueObject https://github.com/martinothamar/WrapperValueObject Why I have not put example: not maintained as in readme -176)Xtz.StronglyTyped https://github.com/dev-experience/Xtz.StronglyTyped +175)Xtz.StronglyTyped https://github.com/dev-experience/Xtz.StronglyTyped Why I have not put example: old ISourceGenerator diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/Facet.md b/v2/rscg_examples_site/docs/RSCG-Examples/Facet.md new file mode 100644 index 000000000..a49edb484 --- /dev/null +++ b/v2/rscg_examples_site/docs/RSCG-Examples/Facet.md @@ -0,0 +1,1106 @@ +--- +sidebar_position: 2290 +title: 229 - Facet +description: Custom generation and mapper +slug: /Facet +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import TOCInline from '@theme/TOCInline'; +import SameCategory from '../Categories/_PrimitiveMapper.mdx'; + +# Facet by Tim Maes + + + + +## NuGet / site data +[![Nuget](https://img.shields.io/nuget/dt/Facet?label=Facet)](https://www.nuget.org/packages/Facet/) +[![GitHub last commit](https://img.shields.io/github/last-commit/Tim-Maes/Facet?label=updated)](https://github.com/Tim-Maes/Facet/) +![GitHub Repo stars](https://img.shields.io/github/stars/Tim-Maes/Facet?style=social) + +## Details + +### Info +:::info + +Name: **Facet** + +Generate lean DTOs, slim views, or faceted projections of your models with a single attribute. + +Author: Tim Maes + +NuGet: +*https://www.nuget.org/packages/Facet/* + + +You can find more details at https://github.com/Tim-Maes/Facet/ + +Source: https://github.com/Tim-Maes/Facet/ + +::: + +### Author +:::note +Tim Maes +![Alt text](https://github.com/Tim-Maes.png) +::: + +### Original Readme +:::note + +
+ Facet logo +
+ +
+"One part of a subject, situation, object that has many parts." +
+ +
+ +
+ +[![NuGet](https://img.shields.io/nuget/v/Facet.svg)](https://www.nuget.org/packages/Facet) +[![Downloads](https://img.shields.io/nuget/dt/Facet.svg)](https://www.nuget.org/packages/Facet) +[![GitHub](https://img.shields.io/github/license/Tim-Maes/Facet.svg)](https://github.com/Tim-Maes/Facet/blob/main/LICENSE.txt) +[![CI](https://github.com/Tim-Maes/Facet/actions/workflows/build.yml/badge.svg)](https://github.com/Tim-Maes/Facet/actions/workflows/build.yml) +[![CD](https://github.com/Tim-Maes/Facet/actions/workflows/release.yml/badge.svg)](https://github.com/Tim-Maes/Facet/actions/workflows/release.yml) + +
+ +--- + +**Facet** is a C# source generator that lets you define **lightweight projections** (DTOs, API models, etc.) directly from your domain models, without writing boilerplate. + +It generates partial classes, records, structs, or record structs with constructors, optional LINQ projections, and even supports custom mappings, all at compile time, with zero runtime cost. + +## :gem: What is Facetting? + +Facetting is the process of defining **focused views** of a larger model at compile time. + +Instead of manually writing separate DTOs, mappers, and projections, **Facet** allows you to declare what you want to keep, and generates everything else. + +You can think of it like **carving out a specific facet** of a gem: + +- The part you care about +- Leaving the rest behind. + +## :grey_question: Why Facetting? + +- Reduce duplication across DTOs, projections, and ViewModels +- Maintain strong typing with no runtime cost +- Stay DRY (Don't Repeat Yourself) without sacrificing performance +- Works seamlessly with LINQ providers like Entity Framework + +## :clipboard: Documentation + +- **[Documentation & Guides](https://github.com/Tim-Maes/Facet//docs/README.md)** +- [What is being generated?](https://github.com/Tim-Maes/Facet//docs/07_WhatIsBeingGenerated.md) + +## :star: Key Features + +- :white_check_mark: Generate classes, records, structs, or record structs from existing types +- :white_check_mark: Exclude fields/properties you don't want (create a Facetted view of your model) +- :white_check_mark: Include/redact public fields +- :white_check_mark: Auto-generate constructors for fast mapping +- :white_check_mark: LINQ projection expressions +- :white_check_mark: Full mapping support with custom mapping configurations +- :white_check_mark: Auto-generate complete CRUD DTO sets with `[GenerateDtos]` +- :white_check_mark: **Expression transformation and mapping utilities** for reusing business logic across entities and DTOs +- :white_check_mark: Preserves member and type XML documentation + +## :earth_americas: The Facet Ecosystem + +Facet is modular and consists of several NuGet packages: + +- **Facet**: The core source generator. Generates DTOs, projections, and mapping code. + +- **Facet.Extensions**: Provider-agnostic extension methods for mapping and projecting (works with any LINQ provider, no EF Core dependency). + +- **Facet.Mapping**: Advanced static mapping configuration support with async capabilities and dependency injection for complex mapping scenarios. + +- **Facet.Mapping.Expressions**: Expression tree transformation utilities for transforming predicates, selectors, and business logic between source entities and their Facet projections. + +- **Facet.Extensions.EFCore**: Async extension methods for Entity Framework Core (requires EF Core 6+). + +## :rocket: Quick start + +### Install the NuGet Package + +``` +dotnet add package Facet +``` + +For LINQ helpers: +``` +dotnet add package Facet.Extensions +``` + +For EF Core support: +``` +dotnet add package Facet.Extensions.EFCore +``` + +For expression transformation utilities: +``` +dotnet add package Facet.Mapping.Expressions +``` + +### Basic Projection +```csharp +[Facet(typeof(User))] +public partial class UserFacet \{ } + +// Auto-generates constructor, properties, and LINQ projection +var user = user.ToFacet(); +var user = user.ToFacet(); //Much faster + +var users = users.SelectFacets(); +var users = users.SelectFacets(); //Much faster +``` + +### Property Exclusion & Field Inclusion +```csharp +// Exclude sensitive properties +string[] excludeFields = \{ "Password", "Email" }; + +[Facet(typeof(User), exclude: excludeFields)] +public partial class UserWithoutEmail \{ } + +// Include public fields +[Facet(typeof(Entity), IncludeFields = true)] +public partial class EntityDto \{ } +``` + +### Different Type Kinds +```csharp +// Generate as record (immutable by default) +[Facet(typeof(Product))] +public partial record ProductDto; + +// Generate as struct (value type) +[Facet(typeof(Point))] +public partial struct PointDto; + +// Generate as record struct (immutable value type) +[Facet(typeof(Coordinates))] +public partial record struct CoordinatesDto; // Preserves required/init-only +``` + +### Custom Sync Mapping +```csharp +public class UserMapper : IFacetMapConfiguration +{ + public static void Map(User source, UserDto target) + { + target.FullName = $"{source.FirstName} {source.LastName}"; + target.Age = CalculateAge(source.DateOfBirth); + } +} + +[Facet(typeof(User), Configuration = typeof(UserMapper))] +public partial class UserDto +{ + public string FullName \{ get; set; } + public int Age \{ get; set; } +} +``` + +### Async Mapping for I/O Operations +```csharp +public class UserAsyncMapper : IFacetMapConfigurationAsync +{ + public static async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default) + { + // Async database lookup + target.ProfilePicture = await GetProfilePictureAsync(source.Id, cancellationToken); + + // Async API call + target.ReputationScore = await CalculateReputationAsync(source.Email, cancellationToken); + } +} + +// Usage +var userDto = await user.ToFacetAsync(); +var userDtos = await users.ToFacetsParallelAsync(); +``` + +### Async Mapping with Dependency Injection +```csharp +public class UserAsyncMapperWithDI : IFacetMapConfigurationAsyncInstance +{ + private readonly IProfilePictureService _profileService; + private readonly IReputationService _reputationService; + + public UserAsyncMapperWithDI(IProfilePictureService profileService, IReputationService reputationService) + { + _profileService = profileService; + _reputationService = reputationService; + } + + public async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default) + { + // Use injected services + target.ProfilePicture = await _profileService.GetProfilePictureAsync(source.Id, cancellationToken); + target.ReputationScore = await _reputationService.CalculateReputationAsync(source.Email, cancellationToken); + } +} + +// Usage with DI +var mapper = new UserAsyncMapperWithDI(profileService, reputationService); +var userDto = await user.ToFacetAsync(mapper); +var userDtos = await users.ToFacetsParallelAsync(mapper); +``` + +### EF Core Integration + +#### Forward Mapping (Entity -> Facet) +```csharp +// Async projection directly in EF Core queries +var userDtos = await dbContext.Users + .Where(u => u.IsActive) + .ToFacetsAsync(); + +// LINQ projection for complex queries +var results = await dbContext.Products + .Where(p => p.IsAvailable) + .SelectFacet() + .OrderBy(dto => dto.Name) + .ToListAsync(); +``` + +#### Reverse Mapping (Facet -> Entity) +```csharp +[Facet(typeof(User)] +public partial class UpdateUserDto \{ } + +[HttpPut("{id}")] +public async Task UpdateUser(int id, UpdateUserDto dto) +{ + var user = await context.Users.FindAsync(id); + if (user == null) return NotFound(); + + // Only updates properties that mutated + user.UpdateFromFacet(dto, context); + + await context.SaveChangesAsync(); + return NoContent(); +} + +// With change tracking for auditing +var result = user.UpdateFromFacetWithChanges(dto, context); +if (result.HasChanges) +{ + logger.LogInformation("User {UserId} updated. Changed: {Properties}", + user.Id, string.Join(", ", result.ChangedProperties)); +} +``` + +### Automatic CRUD DTO Generation + +Generate standard Create, Update, Response, Query, and Upsert DTOs automatically: + +```csharp +// Generate all standard CRUD DTOs +[GenerateDtos(Types = DtoTypes.All, OutputType = OutputType.Record)] +public class User +{ + public int Id \{ get; set; } + public string FirstName \{ get; set; } + public string LastName \{ get; set; } + public string Email \{ get; set; } + public DateTime CreatedAt \{ get; set; } +} + +// Auto-generates: +// - CreateUserRequest (excludes Id) +// - UpdateUserRequest (includes Id) +// - UserResponse (includes all) +// - UserQuery (all properties nullable) +// - UpsertUserRequest (includes Id, for create/update operations) +``` + +#### +Entities with Smart Exclusions +```csharp +[GenerateAuditableDtos( + Types = DtoTypes.Create | DtoTypes.Update | DtoTypes.Response, + OutputType = OutputType.Record, + ExcludeProperties = new[] \{ "Password" })] +public class Product +{ + public int Id \{ get; set; } + public string Name \{ get; set; } + public string Password \{ get; set; \} // Excluded + public DateTime CreatedAt \{ get; set; \} // Auto-excluded (audit) + public string CreatedBy \{ get; set; \} // Auto-excluded (audit) +} + +// Auto-excludes audit fields: CreatedAt, UpdatedAt, CreatedBy, UpdatedBy +``` + +#### Multiple Configurations for Fine-Grained Control +```csharp +// Different exclusions for different DTO types +[GenerateDtos(Types = DtoTypes.Response, ExcludeProperties = new[] \{ "Password", "InternalNotes" })] +[GenerateDtos(Types = DtoTypes.Upsert, ExcludeProperties = new[] \{ "Password" })] +public class Schedule +{ + public int Id \{ get; set; } + public string Name \{ get; set; } + public string Password \{ get; set; \} // Excluded from both + public string InternalNotes \{ get; set; \} // Only excluded from Response +} + +// Generates: +// - ScheduleResponse (excludes Password, InternalNotes) +// - UpsertScheduleRequest (excludes Password, includes InternalNotes) +``` + +#### Perfect for RESTful APIs +```csharp +[HttpPost] +public async Task> CreateSchedule(CreateScheduleRequest request) +{ + var schedule = new Schedule + { + Name = request.Name, + // Map other properties;;; + }; + + context.Schedules.Add(schedule); + await context.SaveChangesAsync(); + return schedule.ToFacet(); +} + +[HttpPut("{id}")] +public async Task> UpsertSchedule(int id, UpsertScheduleRequest body) +{ + var schedule = context.GetScheduleById(id); + if (schedule == null) return NotFound(); + + // Ensure the body ID matches the route ID + body = body with \{ Id = id }; + + schedule.UpdateFromFacet(body, context); + await context.SaveChangesAsync(); + return schedule.ToFacet(); +} +``` + +## :chart_with_upwards_trend: Performance Benchmarks + +Facet delivers competitive performance across different mapping scenarios. Here's how it compares to popular alternatives: + +### Single Mapping + +| Library | Mean Time | Memory Allocated | Performance vs Facet | +|----------|-----------|------------------|---------------------| +| **Facet** | 15.93 ns | 136 B | **Baseline** | +| Mapperly | 15.09 ns | 128 B | 5% faster, 6% less memory | +| Mapster | 21.90 ns | 128 B | 38% slower, 6% less memory | + +### Collection Mapping (10 items) + +| Library | Mean Time | Memory Allocated | Performance vs Facet | +|----------|-----------|------------------|---------------------| +| Mapster | 192.55 ns | 1,416 B | **10% faster, 10% less memory** | +| **Facet** | 207.32 ns | 1,568 B | **Baseline** | +| Mapperly | 222.50 ns | 1,552 B | 7% slower, 1% less memory | + +For this benchmark we used the `` methods. + +**Insights:** +> - **Single mapping**: All three libraries perform similarly with sub-nanosecond differences +> - **Collection mapping**: Mapster has a slight edge for bulk operations, while Facet and Mapperly are very close +> - **Memory efficiency**: All libraries are within ~10% of each other for memory allocation +> - **Compile-time generation**: Both Facet and Mapperly benefit from zero-runtime-cost source generation + + + +::: + +### About +:::note + +Custom generation and mapper + + +::: + +## How to use + +### Example (source csproj, source files) + + + + + +This is the CSharp Project that references **Facet** +```xml showLineNumbers {16} + + + + Exe + net8.0 + enable + enable + + + + true + $(BaseIntermediateOutputPath)\GX + + + + + + + + + + + +``` + + + + + + This is the use of **Facet** in *Program.cs* + +```csharp showLineNumbers +using mapperDemo; +var p=new Person(); +p.FirstName = "Andrei"; +p.LastName = "Ignat"; +PersonDTO dto= new(p); +Console.WriteLine(dto.FullName); + +``` + + + + + This is the use of **Facet** in *Person.cs* + +```csharp showLineNumbers + +public partial class Person +{ + public int ID \{ get; set; } + public string? FirstName \{ get; set; } + public string? LastName \{ get; set; } +} + + +``` + + + + + This is the use of **Facet** in *PersonDTO.cs* + +```csharp showLineNumbers + +using Facet; + +namespace mapperDemo; +[Facet(typeof(Person), Kind = FacetKind.Struct)] +public partial struct PersonDTO +{ public string FullName \{ + get + { + return FirstName + " " + LastName; + } + } +} + +``` + + + + +### Generated Files + +Those are taken from $(BaseIntermediateOutputPath)\GX + + + + +```csharp showLineNumbers +// +// This code was generated by the Facet source generator. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +using System; +using System.Linq.Expressions; + +namespace mapperDemo; +public partial struct PersonDTO +{ + public int ID \{ get; set; } + public string FirstName \{ get; set; } + public string LastName \{ get; set; } + + /// + /// Initializes a new instance of the class from the specified . + /// + /// The source object to copy data from. + public PersonDTO(global::Person source) + { + this.ID = source.ID; + this.FirstName = source.FirstName; + this.LastName = source.LastName; + } + + /// + /// Initializes a new instance of the class with default values. + /// + /// + /// This constructor is useful for unit testing, object initialization, and scenarios + /// where you need to create an empty instance and populate properties later. + /// + public PersonDTO() + { + } + + /// + /// Gets the projection expression for converting to . + /// Use this for LINQ and Entity Framework query projections. + /// + /// An expression tree that can be used in LINQ queries for efficient database projections. + /// + /// + /// var dtos = context.global::Persons + /// .Where(x => x.IsActive) + /// .Select(PersonDTO.Projection) + /// .ToList(); + /// + /// + public static Expression> Projection => + source => new PersonDTO(source); +} + +``` + + + + +```csharp showLineNumbers +// +#pragma warning disable IDE0005 +#pragma warning disable CS0105 +using LightweightObjectMapper; +using System; +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace mapperDemo +{ + sealed partial class Extensions + { + public static partial class Generated + { + /// + /// PostMappingDeclaration for to + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.PostMappingDeclaration, typeof(global::Person), typeof(global::mapperDemo.PersonDTO))] + public static global::mapperDemo.PersonDTO PostMapping_D275C37F33F4AFBD(global::Person source, global::mapperDemo.PersonDTO target) + { + target.ID = source.ID; + return target; + } + } + } +} +``` + + + + +```csharp showLineNumbers +#if !NO_LIGHTWEIGHT_OBJECT_MAPPER_PRE_CODES + +// + +#pragma warning disable IDE0161 // 转换为文件范围限定的 namespace + +using System; +using System.Collections.Generic; + +namespace LightweightObjectMapper +{ + /// + /// 映射配置接口 + /// + internal interface IMappingProfile \{ } + + /// + /// 映射后执行的动作 + /// + /// + /// + internal interface IPostMapping : IMappingProfile + { + /// + /// 映射后执行的动作 + /// + /// + /// + /// + TOut PostMapping(TIn source, TOut target); + } + + /// + /// 映射前准备 + /// + /// + /// + internal interface IMappingPrepare : IMappingProfile + { + /// + /// 映射前准备 + /// + /// + /// + TOut MappingPrepare(TIn source); + } + + /// + /// 接管完整的类型映射(仅非目标实例映射) + /// + /// + /// + internal interface ITypeMapping : IMappingProfile + { + /// + /// 接管完整的类型映射(仅非目标实例映射) + /// + /// + /// + TOut TypeMapping(TIn source); + } + + /// + /// 类型成员忽略映射 + /// + /// + internal interface ITypeMemberIgnoreMapping : IMappingProfile + { + /// + /// 类型成员忽略映射
+ /// 方法体内访问过的 所有成员,将在映射时被忽略 + ///
+ /// + /// + object? IgnoreMapping(T target); + } + + /// + /// 标记一个方法为集合映射方法
+ /// 集合映射方法应包含唯一泛型参数 T ,以及唯一参数 ,返回类型应该为 的派生类型 + ///
+ [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + internal sealed class CollectionMappingAttribute : Attribute + { + } + + /// + /// 标记类为映射配置类 + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + internal sealed class MappingProfileAttribute : Attribute + { + /// + public MappingProfileAttribute() \{ } + } + + /// + /// 映射元数据 + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class MappingMetadataAttribute : Attribute + { + /// + public MappingMetadataAttribute(MappingMetadataType type, params object[] data) \{ } + } + + /// + /// 映射元数据类型 + /// + internal enum MappingMetadataType + { + /// + /// 声明 MappingPrepare + /// + MappingPrepareDeclaration, + + /// + /// 声明 PostMapping + /// + PostMappingDeclaration, + + /// + /// 声明 TypeMapping + /// + TypeMappingDeclaration, + + /// + /// 声明 CollectionMapping + /// + CollectionMappingDeclaration, + + /// + /// 忽略成员声明 + /// + IgnoreMembersDeclaration, + + /// + /// 类型忽略成员声明 + /// + TypeIgnoreMembersDeclaration, + } + + /// + /// 引用其它映射配置类 + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + internal sealed class MappingProfileIncludeAttribute : Attribute + { + /// + public MappingProfileIncludeAttribute(params Type[] profileTypes) \{ } + } + + /// + /// 对象映射 MapTo 占位方法 + /// + [Obsolete("Do not use the placeholder extension class.", true)] + internal static class LightweightObjectMapperPlaceholderExtensions + { + private const string ErrorCallPlaceholderMethodMessage = "Do not use the placeholder extension method. If not redirect to the right mapper extension method please try fix other errors and rebuild the project."; + + /// + /// 对象映射 MapTo 占位方法
+ /// 生成 无需目标对象 的泛型映射方法,映射到 + ///
+ /// + /// + /// + [Obsolete(ErrorCallPlaceholderMethodMessage, true)] + public static TOut MapTo(this object source) + { + throw new NotImplementedException(); + } + + /// + /// 对象映射 MapTo 占位方法
+ /// 生成 需要目标对象 的泛型映射方法,映射到 + ///
+ /// + /// + /// + [Obsolete(ErrorCallPlaceholderMethodMessage, true)] + public static TOut MapTo(this object source, TOut target) + { + throw new NotImplementedException(); + } + + /// + /// 值类型 对象映射 MapTo 占位方法
+ /// 生成 需要目标值类型对象 的泛型映射方法,映射到 + ///
+ /// + /// + /// + [Obsolete(ErrorCallPlaceholderMethodMessage, true)] + public static TOut MapTo(this object source, ref TOut target) + where TOut : struct + { + throw new NotImplementedException(); + } + } +} + +#endif + +``` +
+ + + +```csharp showLineNumbers +#if !NO_LIGHTWEIGHT_OBJECT_MAPPER_PRE_CODES + +// + +#pragma warning disable IDE0161 // 转换为文件范围限定的 namespace + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace LightweightObjectMapper +{ + /// + /// 预定义的类型映射 + /// + [MappingProfile] + internal sealed partial class PredefinedSpecialTypeMapping + : ITypeMapping + , ITypeMapping + , ITypeMapping + { + public bool TypeMapping(int source) + { + return source != 0; + } + + public bool TypeMapping(short source) + { + return source != 0; + } + + bool ITypeMapping.TypeMapping(long source) + { + return source != 0; + } + + [CollectionMapping] + public static IEnumerable? ToIEnumerable(IEnumerable? items) + { + return items?.ToList(); + } + + [CollectionMapping] + public static ICollection? ToICollection(IEnumerable? items) + { + return items?.ToList(); + } + + [CollectionMapping] + public static IReadOnlyCollection? ToIReadOnlyCollection(IEnumerable? items) + { + return items?.ToList(); + } + + [CollectionMapping] + public static IList? ToIList(IEnumerable? items) + { + return items?.ToList(); + } + + [CollectionMapping] + public static IReadOnlyList? ToIReadOnlyList(IEnumerable? items) + { + return items?.ToList(); + } + + [CollectionMapping] + public static List? ToList(IEnumerable? items) + { + return items?.ToList(); + } + } +} + +#endif + +``` + + + + +```csharp showLineNumbers +// +#pragma warning disable IDE0005 +#pragma warning disable CS0105 +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace LightweightObjectMapper +{ + internal static partial class LOMMapExtensions_mapperDemo + { + /// + /// Map to the following types:
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TOut MapTo(this global::Person source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (typeof(TOut) == typeof(global::mapperDemo.PersonDTO)) + { + var target = new global::mapperDemo.PersonDTO() + { + LastName = source.LastName, + FirstName = source.FirstName, + ID = source.ID, + }; + target = global::mapperDemo.Extensions.Generated.PostMapping_D275C37F33F4AFBD(source, target); + return (TOut)(target as object); + } + + throw new global::System.NotImplementedException($"No mapping code for {typeof(TOut)}."); + } + } +} +``` +
+ + + +```csharp showLineNumbers +// +#pragma warning disable IDE0005 +#pragma warning disable CS0105 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace LightweightObjectMapper +{ + sealed partial class PredefinedSpecialTypeMapping + { + public static partial class Generated + { + /// + /// TypeMappingDeclaration for to + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.TypeMappingDeclaration, typeof(int), typeof(bool))] + public static bool TypeMapping_A07AFC9A322FFA04(int source) + { + return source != 0; + } + + /// + /// TypeMappingDeclaration for to + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.TypeMappingDeclaration, typeof(short), typeof(bool))] + public static bool TypeMapping_946949E7222BC174(short source) + { + return source != 0; + } + + /// + /// TypeMappingDeclaration for to + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.TypeMappingDeclaration, typeof(long), typeof(bool))] + public static bool TypeMapping_3C4D395B4EF43E87(long source) + { + return source != 0; + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.IEnumerable<>))] + public static global::System.Collections.Generic.IEnumerable CollectionMapping_CEFAD35E246FD0F7(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.ICollection<>))] + public static global::System.Collections.Generic.ICollection CollectionMapping_37FFD1A2226B51E9(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.IReadOnlyCollection<>))] + public static global::System.Collections.Generic.IReadOnlyCollection CollectionMapping_AF82A9960EE0C495(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.IList<>))] + public static global::System.Collections.Generic.IList CollectionMapping_284BCB723CA17B0E(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.IReadOnlyList<>))] + public static global::System.Collections.Generic.IReadOnlyList CollectionMapping_976EA1DB5B772C59(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + + /// + /// CollectionMappingDeclaration for + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MappingMetadata(MappingMetadataType.CollectionMappingDeclaration, typeof(global::System.Collections.Generic.List<>))] + public static global::System.Collections.Generic.List CollectionMapping_070F0D0F908DAF14(global::System.Collections.Generic.IEnumerable items) + { + return items?.ToList(); + } + } + } +} +``` + + + +
+## Useful + +### Download Example (.NET C#) + +:::tip + +[Download Example project Facet ](/sources/Facet.zip) + +::: + + +### Share Facet + + + +https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet + + + diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md index ff073792e..2a696226f 100644 --- a/v2/rscg_examples_site/docs/RSCG-Examples/index.md +++ b/v2/rscg_examples_site/docs/RSCG-Examples/index.md @@ -1,7 +1,7 @@ --- sidebar_position: 30 -title: 228 RSCG list by category -description: 228 RSCG list by category +title: 229 RSCG list by category +description: 229 RSCG list by category slug: /rscg-examples --- @@ -1053,7 +1053,7 @@ import DocCardList from '@theme/DocCardList'; ## Mapper
- Expand Mapper =>examples:8 + Expand Mapper =>examples:9 @@ -1094,6 +1094,11 @@ import DocCardList from '@theme/DocCardList'; [LightweightObjectMapper](/docs/LightweightObjectMapper) + + + +[Facet](/docs/Facet) +
@@ -1790,6 +1795,8 @@ flowchart LR; Mapper--> LightweightObjectMapper((LightweightObjectMapper)) + Mapper--> Facet((Facet)) + Mediator--> Mediator((Mediator)) Mediator--> DeeDee((DeeDee)) diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md index 9ae6f98ae..dcac293bb 100644 --- a/v2/rscg_examples_site/docs/about.md +++ b/v2/rscg_examples_site/docs/about.md @@ -6,7 +6,7 @@ title: About ## Content You will find here code examples -of 228 Roslyn Source Code Generator (RSCG) +of 229 Roslyn Source Code Generator (RSCG) that can be useful for you. That means, you will write more elegant and concise code - even if the generators code is not always nice to look. ## Are those examples ready for production? diff --git a/v2/rscg_examples_site/docs/indexRSCG.md b/v2/rscg_examples_site/docs/indexRSCG.md index ff1d7b4f8..ce76dffa8 100644 --- a/v2/rscg_examples_site/docs/indexRSCG.md +++ b/v2/rscg_examples_site/docs/indexRSCG.md @@ -7,9 +7,9 @@ slug: /List-of-RSCG import useBaseUrl from '@docusaurus/useBaseUrl'; -## 228 RSCG with examples in descending chronological order +## 229 RSCG with examples in descending chronological order -This is the list of 228 ( 14 from Microsoft) RSCG with examples +This is the list of 229 ( 14 from Microsoft) RSCG with examples [See by category](/docs/rscg-examples) [See as json](/exports/RSCG.json) [See as Excel](/exports/RSCG.xlsx) @@ -20,6 +20,7 @@ This is the list of 228 ( 14 from Microsoft) RSCG with examples | No | Name | Date | Category | | --------- | ----- | ---- | -------- | +|229| [Facet by Tim Maes ](/docs/Facet)|2025-08-17 => 17 August 2025 | [Mapper](/docs/Categories/Mapper) | |228| [mvvmgen by Thomas Claudius Huber ](/docs/mvvmgen)|2025-08-16 => 16 August 2025 | [Serializer](/docs/Categories/Serializer) | |227| [UtilityVerse.Copy by pritom purkayasta ](/docs/UtilityVerse.Copy)|2025-08-15 => 15 August 2025 | [Clone](/docs/Categories/Clone) | |226| [requiredenum by Yaroslav ](/docs/requiredenum)|2025-08-14 => 14 August 2025 | [Enum](/docs/Categories/Enum) | diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js index 59400ada4..7f5d335ee 100644 --- a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js +++ b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js @@ -4,7 +4,7 @@ import styles from './styles.module.css'; const FeatureList = [ { -title: '228 Examples (14 from MSFT)', +title: '229 Examples (14 from MSFT)', Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, description: ( <> diff --git a/v2/rscg_examples_site/static/exports/RSCG.json b/v2/rscg_examples_site/static/exports/RSCG.json index df079b290..7fcef4571 100644 --- a/v2/rscg_examples_site/static/exports/RSCG.json +++ b/v2/rscg_examples_site/static/exports/RSCG.json @@ -1825,6 +1825,14 @@ "Source": "https://github.com/thomasclaudiushuber/mvvmgen", "Category": "Serializer", "AddedOn": "2025-08-16T00:00:00" + }, + { + "Name": "Facet", + "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet", + "NuGet": "https://www.nuget.org/packages/Facet/", + "Source": "https://github.com/Tim-Maes/Facet/", + "Category": "Mapper", + "AddedOn": "2025-08-17T00:00:00" } ] } \ No newline at end of file diff --git a/v2/rscg_examples_site/static/exports/RSCG.xlsx b/v2/rscg_examples_site/static/exports/RSCG.xlsx index 78b2dc051..8f3b99f73 100644 Binary files a/v2/rscg_examples_site/static/exports/RSCG.xlsx and b/v2/rscg_examples_site/static/exports/RSCG.xlsx differ diff --git a/v2/rscg_examples_site/static/sources/Facet.zip b/v2/rscg_examples_site/static/sources/Facet.zip new file mode 100644 index 000000000..a68a4fc88 Binary files /dev/null and b/v2/rscg_examples_site/static/sources/Facet.zip differ