diff --git a/README.md b/README.md index a4bcc1532..3db132589 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# RSCG - 271 Examples of Roslyn Source Code Generators / 16 created by Microsoft / +# RSCG - 272 Examples of Roslyn Source Code Generators / 16 created by Microsoft / -The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 271 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. +The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 272 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. This system serves as both a learning resource for .NET developers interested in source generators and an automated pipeline for maintaining up-to-date documentation about the RSCG ecosystem -## Latest Update : 2026-05-16 => 16 May 2026 +## Latest Update : 2026-06-29 => 29 June 2026 If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)*** @@ -24,8 +24,30 @@ If you want to be notified each time I add a new RSCG example , please click htt ## Content -Those are the 271 Roslyn Source Code Generators that I have tested you can see and download source code example. +Those are the 272 Roslyn Source Code Generators that I have tested you can see and download source code example. ( including 16 from Microsoft ) +### 272. [AlephMapper](https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper) , in the [Database](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#database) category + +Generated on : 2026-06-29 => 29 June 2026 + +
+ Expand + + + +Author: Yevhen Cherkes + +Source generator for creating projectable companions + +Nuget: [https://www.nuget.org/packages/AlephMapper/](https://www.nuget.org/packages/AlephMapper/) + + +Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper](https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper) + +Source: [https://github.com/Raffinert/AlephMapper](https://github.com/Raffinert/AlephMapper) + +
+ ### 271. [TypedStateBuilder.Generator](https://ignatandrei.github.io/RSCG_Examples/v2/docs/TypedStateBuilder.Generator) , in the [Builder](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#builder) category Generated on : 2026-05-16 => 16 May 2026 diff --git a/later.md b/later.md index 4e77a68db..8ee650ce5 100644 --- a/later.md +++ b/later.md @@ -1,6 +1,6 @@ # Just later -## Latest Update : 2026-05-16 => 16 May 2026 +## Latest Update : 2026-06-29 => 29 June 2026 diff --git a/v2/Generator/MultiGeneratorV2.cs b/v2/Generator/MultiGeneratorV2.cs index 59bf0fa19..a1b9ece95 100644 --- a/v2/Generator/MultiGeneratorV2.cs +++ b/v2/Generator/MultiGeneratorV2.cs @@ -118,6 +118,9 @@ public string[] SourceNoRSCG() if (File.Exists(nameFile)) { var text=await File.ReadAllTextAsync(nameFile); + text = text.Replace("(examples/", $"({d.Generator!.Source}/examples/"); + text = text.Replace("(tests/", $"({d.Generator!.Source}/tests/"); + text = text.Replace("(/.github/", $"({d.Generator!.Source}/.github/"); text = text.Replace("(BenchmarkDotNet", $"({d.Generator!.Source}/BenchmarkDotNet"); text = text.Replace("(./BoolParameterGenerator.Github.Example/", $"({d.Generator!.Source}/BoolParameterGenerator.Github.Example/"); diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv index 204fead0a..a80fea266 100644 --- a/v2/Generator/all.csv +++ b/v2/Generator/all.csv @@ -270,3 +270,4 @@ Nr,Key,Source,Category 269,LinkDotNet.Enumeration, https://github.com/linkdotnet/Enumeration,Enum 270,AssemblyMetadata.Generators, https://github.com/loresoft/AssemblyMetadata.Generators,EnhancementProject 271,TypedStateBuilder.Generator, https://github.com/Georgiy-Petrov/TypedStateBuilder.Generator,Builder +272,AlephMapper, https://github.com/Raffinert/AlephMapper,Database diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json index 03302b860..d8a16c66c 100644 --- a/v2/RSCGExamplesData/GeneratorDataRec.json +++ b/v2/RSCGExamplesData/GeneratorDataRec.json @@ -1642,5 +1642,11 @@ "Category": 4, "dtStart": "2026-05-16T00:00:00", "show": true + }, + { + "ID":"AlephMapper", + "Category": 14, + "dtStart": "2026-06-29T00:00:00", + "show": true } ] \ No newline at end of file diff --git a/v2/book/examples/AlephMapper.html b/v2/book/examples/AlephMapper.html new file mode 100644 index 000000000..569c2cfa0 --- /dev/null +++ b/v2/book/examples/AlephMapper.html @@ -0,0 +1,70 @@ + +

RSCG nr 272 : AlephMapper

+ +

Info

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

You can find more details at : https://github.com/Raffinert/AlephMapper

+ +

Author :Yevhen Cherkes

+ +

Source: https://github.com/Raffinert/AlephMapper

+ +

About

+ +AlephMapper helps you define LINQ-translatable object mappers for EF Core.It turns a normal C# mapping method into an expression that can be embedded in queries, so your entity-to-DTO projection can run inside the database query instead of after materialization.This is useful for cleaner projection code, avoiding hand-written select expressions, and keeping mapping logic reusable across queries. + +

+ How to use +

+

+ Add reference to the AlephMapper in the csproj +

+ + +

This was for me the starting code

+ +
+ I have coded the file Program.cs +
+ +
+ +
+ I have coded the file ProjectExtensions.cs +
+ +
+ +
+ I have coded the file ProjectDTO.cs +
+ +
+

And here are the generated files

+ +
+ The file generated is AlephMapper.Attributes.g.cs +
+ + +
+ The file generated is EntityDemo_ProjectExtensions_GeneratedMappings.g.cs +
+ + +

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

+ + +

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

+ diff --git a/v2/book/examples/TypedStateBuilder.Generator.html b/v2/book/examples/TypedStateBuilder.Generator.html index 26bebbf51..875c7f672 100644 --- a/v2/book/examples/TypedStateBuilder.Generator.html +++ b/v2/book/examples/TypedStateBuilder.Generator.html @@ -12,7 +12,7 @@

Info

About

-Generate strongly typed state builders for C# applications, improving code readability and maintainability. +Generate strongly typed state builders for C# applications, enforced at compile time**Summary of TypedStateBuilder.Generator****Purpose:** Generates compile-time safe step-by-step builders using the *type-state pattern* — each required property must be set in order before Build() is available, enforced at compile time.**NuGet:** https://www.nuget.org/packages/TypedStateBuilder.Generator/**GitHub:** https://github.com/Georgiy-Petrov/TypedStateBuilder.Generator**Author:** Georgiy Petrov**How to use:**1. Decorate a builder class with [TypedStateBuilder], mark each required step with [StepForValue], and optionally add [ValidateValue] for validation:```csharp[TypedStateBuilder]public class PersonBuilder{ [StepForValue] [ValidateValue(nameof(ValidateName))] private string lastName = string.Empty; [StepForValue] [ValidateValue(nameof(ValidateName))] private string firstName = string.Empty; public void ValidateName(string name) { if (string.IsNullOrWhiteSpace(name) || name.Length <= 1) throw new ArgumentException("Name must be at least 2 characters long."); } [Build] public Person Build() => new Person(firstName, lastName);}```2. Use the generated fluent builder — steps are enforced in order at compile time:```csharpvar p = TypedStateBuilders .CreatePersonBuilder() .SetFirstName("Andrei") .SetLastName("Ignat") .Build();Console.WriteLine(p.FullName()); // "Andrei Ignat"```

How to use diff --git a/v2/book/list.html b/v2/book/list.html index 502a2e64f..72f699aab 100644 --- a/v2/book/list.html +++ b/v2/book/list.html @@ -17,7 +17,7 @@

-This is the list of 271 RSCG with examples => +This is the list of 272 RSCG with examples =>

@@ -1110,6 +1110,10 @@

+ + + +
271 TypedStateBuilder.Generator
272AlephMapper
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml index 795799838..b02ef9ebf 100644 --- a/v2/book/pandocHTML.yaml +++ b/v2/book/pandocHTML.yaml @@ -285,6 +285,7 @@ input-files: - examples/LinkDotNet.Enumeration.html - examples/AssemblyMetadata.Generators.html - examples/TypedStateBuilder.Generator.html +- examples/AlephMapper.html # or you may use input-file: with a single value # defaults: diff --git a/v2/docFind.json b/v2/docFind.json index 2189955b9..1e81f34fd 100644 --- a/v2/docFind.json +++ b/v2/docFind.json @@ -1624,5 +1624,11 @@ "category": "Builder", "href": "/RSCG_Examples/v2/docs/TypedStateBuilder.Generator/", "body": "A Roslyn source generator that produces **compile-time safe builders** using the *type-state pattern*." + }, + { + "title": "AlephMapper", + "category": "Database", + "href": "/RSCG_Examples/v2/docs/AlephMapper/", + "body": "Source generator for creating projectable companions" } ] \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/description.json b/v2/rscg_examples/AlephMapper/description.json new file mode 100644 index 000000000..82d4b3b4f --- /dev/null +++ b/v2/rscg_examples/AlephMapper/description.json @@ -0,0 +1,25 @@ +{ + "generator":{ + "name":"AlephMapper", + "nuget":[ + "https://www.nuget.org/packages/AlephMapper/" + ], + "link":"https://github.com/Raffinert/AlephMapper", + "author":"Yevhen Cherkes", + "source":"https://github.com/Raffinert/AlephMapper" + }, + "data":{ + "goodFor":["AlephMapper helps you define LINQ-translatable object mappers for EF Core.", + "It turns a normal C# mapping method into an expression that can be embedded in queries, so your entity-to-DTO projection can run inside the database query instead of after materialization.", + "This is useful for cleaner projection code, avoiding hand-written select expressions, and keeping mapping logic reusable across queries." + ], + "csprojDemo":"EntityDemo.csproj", + "csFiles":["Program.cs","ProjectExtensions.cs","ProjectDTO.cs"], + "excludeDirectoryGenerated":[""], + "includeAdditionalFiles":[""] + }, + "links":{ + "blog":"", + "video":"" + } +} \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/nuget.txt b/v2/rscg_examples/AlephMapper/nuget.txt new file mode 100644 index 000000000..72581930d --- /dev/null +++ b/v2/rscg_examples/AlephMapper/nuget.txt @@ -0,0 +1 @@ +Source generator for creating projectable companions \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/readme.txt b/v2/rscg_examples/AlephMapper/readme.txt new file mode 100644 index 000000000..ada80a995 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/readme.txt @@ -0,0 +1,303 @@ +[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://stand-with-ukraine.pp.ua) + +## Terms of use[?](https://github.com/Tyrrrz/.github/blob/master/docs/why-so-political.md) + +By using this project or its source code, for any purpose and in any shape or form, you grant your **implicit agreement** to all the following statements: + +- You **condemn Russia and its military aggression against Ukraine** +- You **recognize that Russia is an occupant that unlawfully invaded a sovereign state** +- You **support Ukraine's territorial integrity, including its claims over temporarily occupied territories of Crimea and Donbas** +- You **reject false narratives perpetuated by Russian state propaganda** + +To learn more about the war and how you can help, [click here](https://stand-with-ukraine.pp.ua). Glory to Ukraine! + +# AlephMapper + +[![NuGet](https://img.shields.io/nuget/v/AlephMapper.svg)](https://www.nuget.org/packages/AlephMapper) +[![NuGet Downloads](https://img.shields.io/nuget/dt/AlephMapper.svg)](https://www.nuget.org/packages/AlephMapper) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +AlephMapper is a C# source generator for reusable manual mappings. Write one expression-bodied mapping method and generate companion methods for EF Core projections and optional update-in-place mapping from the same code. + +Use it when you want hand-written mapping logic without maintaining a separate `Expression>` for queries or a second method for updating existing objects. + +## Features + +- **Single-source mappings** - use one mapping method for runtime mapping, query projection, and update variants. +- **EF Core-friendly projections** - generate `Expression>` methods for `.Select(...)`. +- **Method inlining** - reuse small helper methods in mappings and let AlephMapper inline them into generated code. +- **Configurable null handling** - choose how null-conditional access (`?.`) is handled in generated expressions. +- **Update-in-place mapping** - mutate existing destination instances, including EF Core tracked entities. + +## Install + +Using the .NET CLI: + +```bash +dotnet add package AlephMapper +``` + +Using `PackageReference`: + +```xml + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +``` + +With Central Package Management: + +```xml + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +``` + +When referencing AlephMapper directly from source, add it as an analyzer-only project reference: + +```xml + +``` + +`PrivateAssets="all"` keeps AlephMapper from flowing transitively to consumers of your library, and `IncludeAssets` ensures the analyzer/source-generator assets are available at build time. + +## Requirements + +- Mapping methods must be `static`, expression-bodied, and declared in a `static partial` class. + +## Quick Start + +Add `using AlephMapper;`, then mark a mapping method or mapper class with `[Expressive]`. + +```csharp +using AlephMapper; + +public static partial class PersonMapper +{ + [Expressive] + public static PersonDto MapToPerson(Employee employee) => new() + { + Id = employee.EmployeeId, + FullName = GetFullName(employee), + Email = employee.ContactInfo.Email, + Department = employee.Department.Name + }; + + public static string GetFullName(Employee employee) => + employee.FirstName + " " + employee.LastName; +} +``` + +AlephMapper generates a projection companion method: + +```csharp +public static partial class PersonMapper +{ + public static Expression> MapToPersonExpression() => + employee => new PersonDto + { + Id = employee.EmployeeId, + FullName = employee.FirstName + " " + employee.LastName, + Email = employee.ContactInfo.Email, + Department = employee.Department.Name + }; +} +``` + +Use the generated expression in EF Core queries: + +```csharp +var people = await dbContext.Employees + .Select(PersonMapper.MapToPersonExpression()) + .ToListAsync(); +``` + +Use the original method for in-memory mapping: + +```csharp +var employee = GetEmployee(); +var dto = PersonMapper.MapToPerson(employee); +``` + +## Null Handling + +C# null-conditional access (`?.`) is not directly supported in expression trees. AlephMapper lets you choose how to handle it. + +```csharp +[Expressive(NullConditionalRewrite = NullConditionalRewrite.Rewrite)] +public static partial class PersonMapper +{ + public static PersonSummary GetSummary(Person person) => new() + { + Name = person.Name, + City = person.Address?.City, + HasAddress = person.Address != null + }; +} +``` + +Available policies: + +| Policy | Behavior | +| --- | --- | +| `None` | Reports unsupported null-conditional usage. | +| `Ignore` | Removes null-conditional access, for example `person.Address?.City` becomes `person.Address.City`. | +| `Rewrite` | Emits explicit null checks, for example `person.Address != null ? person.Address.City : null`. | + +`Ignore` is the default. Use `Rewrite` when you want generated expressions to preserve null-safe behavior. + +## Update Existing Objects + +Mark a mapping with `[Updatable]` to generate an overload that writes into an existing destination instance. + +```csharp +using AlephMapper; + +public static partial class PersonMapper +{ + [Updatable] + public static Person MapToPerson(PersonUpdateDto dto) => new() + { + FirstName = dto.FirstName, + LastName = dto.LastName, + Email = dto.Email + }; +} +``` + +Generated usage: + +```csharp +var person = await db.People.FindAsync(id); + +PersonMapper.MapToPerson(dto, target: person); + +await db.SaveChangesAsync(); +``` + +This is useful with EF Core change tracking because the tracked entity instance is preserved. + +Collection properties are skipped by default. To update collections, configure the policy: + +```csharp +[Updatable(CollectionProperties = CollectionPropertiesPolicy.Update)] +public static Person MapToPerson(PersonUpdateDto dto) => new() +{ + Orders = dto.Orders.Select(OrderMapper.MapToOrder).ToList() +}; +``` + +## How It Works + +For each `[Expressive]` method, AlephMapper generates a method named `Expression()` returning `Expression>`. + +```csharp +public static PersonDto MapToPerson(Employee employee) => ... + +public static Expression> MapToPersonExpression() => ... +``` + +For each `[Updatable]` method, AlephMapper generates an overload with a `target` parameter. + +```csharp +public static PersonDto MapToPerson(Employee employee) => ... + +public static PersonDto MapToPerson(Employee employee, PersonDto target) => ... +``` + +Helper methods in the same mapper class are inlined where possible. + +## Supported Mapping Shape + +Methods must be: + +- expression-bodied (`=>`) +- `static` +- declared in a `static partial` class +- visible to the source generator in the current compilation + +AlephMapper is best suited for object initializer mappings and small helper methods that can be inlined into generated expressions. + +## Troubleshooting + +### Generated method is missing + +Check that the mapper class is `static partial`, the method is expression-bodied, and the method or containing class has `[Expressive]` or `[Updatable]`. + +### NullReferenceException after using `?.` + +The default null policy is `Ignore`, which removes null-conditional access in generated expressions. Use: + +```csharp +[Expressive(NullConditionalRewrite = NullConditionalRewrite.Rewrite)] +``` + +### Updatable mapping is skipped for value types + +`[Updatable]` is intended for reference-type destinations. Value types are copied by value, so update-in-place semantics are not useful. + +### Circular helper calls + +Generation is skipped when AlephMapper detects circular references between mapping/helper methods. Break the cycle or keep part of the logic outside the generated mapping path. + +### Inspect generated code + +Add this to your project file: + +```xml + + true + +``` + +Generated files are emitted under the compiler-generated files output directory for your project. + +## Comparison + +| Tool | Main style | AlephMapper difference | +| --- | --- | --- | +| AutoMapper | Runtime configuration and conventions | AlephMapper keeps mappings as ordinary C# methods. | +| Mapster | Convention/configuration mapping with code generation options | AlephMapper focuses on source-generating companions from manual mappings. | +| EntityFrameworkCore.Projectables | Projectable members for EF Core | AlephMapper targets mapping methods and update overloads. | +| Expressionify | Expression expansion | AlephMapper is mapping-oriented and also supports update-in-place generation. | + +## Examples + +- [Sample app](examples/SampleApp) - richer mapping examples. +- [Integration tests](tests/AlephMapper.IntegrationTests) - EF Core and generated behavior coverage. + +## Contributing + +Contributions are welcome. + +1. Fork the repository. +2. Create a feature branch. +3. Make the change. +4. Add or update tests. +5. Run the test suite. +6. Open a pull request. + +## License + +This project is licensed under the MIT License. See [LICENSE](LICENSE) for details. + +## Acknowledgments + +- Inspired by [EntityFrameworkCore.Projectables](https://github.com/koenbeuk/EntityFrameworkCore.Projectables) and [Expressionify](https://github.com/ClaveConsulting/Expressionify). +- Thanks to all [contributors](https://github.com/Raffinert/AlephMapper/graphs/contributors). + +## Related Projects + +- [EntityFrameworkCore.Projectables](https://github.com/koenbeuk/EntityFrameworkCore.Projectables) +- [Expressionify](https://github.com/ClaveConsulting/Expressionify) +- [AutoMapper](https://automapper.org/) +- [Mapster](https://github.com/MapsterMapper/Mapster) +- [Facet](https://github.com/Tim-Maes/Facet) diff --git a/v2/rscg_examples/AlephMapper/src/.tours/AlephMapper.tour b/v2/rscg_examples/AlephMapper/src/.tours/AlephMapper.tour new file mode 100644 index 000000000..9199648e4 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/.tours/AlephMapper.tour @@ -0,0 +1,48 @@ + +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "AlephMapper", + "steps": + [ + { + "file": "EntityDemo/EntityDemo.csproj", + "description": "First, we add Nuget [AlephMapper](https://www.nuget.org/packages/AlephMapper/) in csproj ", + "pattern": "AlephMapper" + } + + ,{ + "file": "EntityDemo/ProjectDTO.cs", + "description": "File ProjectDTO.cs ", + "pattern": "this is the code" + } + + ,{ + "file": "EntityDemo/ProjectExtensions.cs", + "description": "File ProjectExtensions.cs ", + "pattern": "this is the code" + } + + ,{ + "file": "EntityDemo/Program.cs", + "description": "File Program.cs \r\n>> dotnet run --project EntityDemo/EntityDemo.csproj ", + "pattern": "this is the code" + } + + + ,{ + "file": "EntityDemo/obj/GX/AlephMapper/AlephMapper.AlephSourceGenerator/EntityDemo_ProjectExtensions_GeneratedMappings.g.cs", + "description": "Generated File 2 from 2 : EntityDemo_ProjectExtensions_GeneratedMappings.g.cs ", + "line": 1 + } + + ,{ + "file": "EntityDemo/obj/GX/AlephMapper/AlephMapper.AlephSourceGenerator/AlephMapper.Attributes.g.cs", + "description": "Generated File 1 from 2 : AlephMapper.Attributes.g.cs ", + "line": 1 + } + + ], + + "ref": "main" + +} \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo.sln b/v2/rscg_examples/AlephMapper/src/EntityDemo.sln new file mode 100644 index 000000000..29ae3a882 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35806.99 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityDemo", "EntityDemo\EntityDemo.csproj", "{8E781946-11BC-4A8C-857F-E1770565823D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E781946-11BC-4A8C-857F-E1770565823D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E781946-11BC-4A8C-857F-E1770565823D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E781946-11BC-4A8C-857F-E1770565823D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E781946-11BC-4A8C-857F-E1770565823D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F93AFAB4-9779-49B1-A381-7C4845EA2ED1} + EndGlobalSection +EndGlobal diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/DotNetStatsContext.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/DotNetStatsContext.cs new file mode 100644 index 000000000..b414c6ffe --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/DotNetStatsContext.cs @@ -0,0 +1,42 @@ + +namespace Stats.Database; +public partial class DotNetStatsContext : DbContext +{ + internal DotNetStatsContext() : base() { } + public DotNetStatsContext(DbContextOptions options) + : base(options) + { + + } + + public virtual DbSet Projects { get; set; } + + public virtual DbSet Stars { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + //entity.ToTable("Project"); + + //entity.Property(e => e.Id).HasColumnName("ID"); + entity.Property(e => e.Description).HasMaxLength(500); + entity.Property(e => e.Name).HasMaxLength(50); + entity.Property(e => e.SourceCodeUrl).HasMaxLength(50); + }); + + modelBuilder.Entity(entity => + { + + entity.HasOne(d => d.IdprojectNavigation) + .WithMany(p => p.Stars) + .HasForeignKey(d => d.Idproject) + .OnDelete(DeleteBehavior.ClientSetNull) + ; + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/EntityDemo.csproj b/v2/rscg_examples/AlephMapper/src/EntityDemo/EntityDemo.csproj new file mode 100644 index 000000000..ee4643f96 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/EntityDemo.csproj @@ -0,0 +1,27 @@ + + + + Exe + net10.0 + enable + enable + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + true + $(BaseIntermediateOutputPath)\GX + + diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/Program.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/Program.cs new file mode 100644 index 000000000..0b93e8a54 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/Program.cs @@ -0,0 +1,16 @@ +using EntityDemo; + +Console.WriteLine("Hello, World!"); +DbContextOptionsBuilder optionsBuilder = new(); +//optionsBuilder.UseInMemoryDatabase("StatsDatabase"); +optionsBuilder.UseSqlite("Data Source=stats.db"); +var cnt = new DotNetStatsContext(optionsBuilder.Options); +await cnt.Database.EnsureCreatedAsync(); +Console.WriteLine("Database created"); + +var projDTO = cnt.Projects + .Select(ProjectExtensions.ProjToDTOExpression()); +Console.WriteLine(projDTO.ToQueryString()); +var result = await projDTO.ToArrayAsync(); + +Console.WriteLine(result.Length); \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/Project.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/Project.cs new file mode 100644 index 000000000..a26e58371 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/Project.cs @@ -0,0 +1,14 @@ +namespace Stats.Database; + +public partial class Project +{ + public long Id { get; set; } + + public string Name { get; set; } = null!; + + public string SourceCodeUrl { get; set; } = null!; + + public string? Description { get; set; } + + public ICollection Stars { get; set; } = new List(); +} diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectDTO.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectDTO.cs new file mode 100644 index 000000000..5eedcfe0a --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectDTO.cs @@ -0,0 +1,7 @@ +namespace EntityDemo; + +public class ProjectDTO +{ + public string? Name { get; set; } + public int CountStars { get; set; } +} diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectExtensions.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectExtensions.cs new file mode 100644 index 000000000..a8a6417aa --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/ProjectExtensions.cs @@ -0,0 +1,14 @@ +namespace EntityDemo; +public static partial class ProjectExtensions +{ + [AlephMapper.Expressive(NullConditionalRewrite = AlephMapper.NullConditionalRewrite.Rewrite)] + public static ProjectDTO ProjToDTO(Stats.Database.Project project) => + + new ProjectDTO + { + Name = project.Name, + CountStars = project.Stars?.Count ?? 0 + } + ; + +} \ No newline at end of file diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/Star.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/Star.cs new file mode 100644 index 000000000..ab921dad5 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/Star.cs @@ -0,0 +1,15 @@ +namespace Stats.Database; + +public partial class Star +{ + public long Id { get; set; } + + public long Idproject { get; set; } + + public int Count { get; set; } + + public DateOnly DateRecording { get; set; } + + public Project IdprojectNavigation { get; set; } = null!; + +} diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/globals.cs b/v2/rscg_examples/AlephMapper/src/EntityDemo/globals.cs new file mode 100644 index 000000000..0d211d114 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/src/EntityDemo/globals.cs @@ -0,0 +1,2 @@ +global using Microsoft.EntityFrameworkCore; +global using Stats.Database; diff --git a/v2/rscg_examples/AlephMapper/src/EntityDemo/stats.db b/v2/rscg_examples/AlephMapper/src/EntityDemo/stats.db new file mode 100644 index 000000000..eaf71a548 Binary files /dev/null and b/v2/rscg_examples/AlephMapper/src/EntityDemo/stats.db differ diff --git a/v2/rscg_examples/AlephMapper/video.json b/v2/rscg_examples/AlephMapper/video.json new file mode 100644 index 000000000..3d6ec6a02 --- /dev/null +++ b/v2/rscg_examples/AlephMapper/video.json @@ -0,0 +1,39 @@ +{ + "scriptName": "AlephMapper", + "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 AlephMapper . AlephMapper helps you define LINQ-translatable object mappers for EF Core.It turns a normal C# mapping method into an expression that can be embedded in queries, so your entity-to-DTO projection can run inside the database query instead of after materialization.This is useful for cleaner projection code, avoiding hand-written select expressions, and keeping mapping logic reusable across queries. ."}, +{"typeStep":"browser","arg":"https://www.nuget.org/packages/AlephMapper/"}, +{"typeStep":"text","arg": "The whole example is here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper"}, +{"typeStep":"text","arg": "You can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper#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\\AlephMapper\\src\\EntityDemo.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\\AlephMapper\\src"}, + +{"typeStep":"text","arg": "To use it ,you will put the Nuget AlephMapper into the csproj "}, + +{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\AlephMapper\\src\\EntityDemo\\EntityDemo.csproj"}, + +{"typeStep":"text","arg": "And now I will show you an example of using AlephMapper"}, + +{"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":"EntityDemo.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/AlephMapper#download-example-net--c-", +SpeakTest=" "}, +{"typeStep":"waitseconds","arg":"30"}, +] +} diff --git a/v2/rscg_examples_site/docs/Authors/Yevhen_Cherkes.md b/v2/rscg_examples_site/docs/Authors/Yevhen_Cherkes.md new file mode 100644 index 000000000..a1c231c6a --- /dev/null +++ b/v2/rscg_examples_site/docs/Authors/Yevhen_Cherkes.md @@ -0,0 +1,7 @@ +# Author : Yevhen Cherkes + +Number RSCG: 1 + + + 1 [AlephMapper](/docs/AlephMapper) [![Nuget](https://img.shields.io/nuget/dt/AlephMapper?label=AlephMapper)](https://www.nuget.org/packages/AlephMapper/) ![GitHub Repo stars](https://img.shields.io/github/stars/Raffinert/AlephMapper?style=social) 2026-06-29 + diff --git a/v2/rscg_examples_site/docs/Categories/Database.md b/v2/rscg_examples_site/docs/Categories/Database.md index 469cfc5ba..7ce411cf1 100644 --- a/v2/rscg_examples_site/docs/Categories/Database.md +++ b/v2/rscg_examples_site/docs/Categories/Database.md @@ -1,20 +1,22 @@

Database

-Number RSCG: 8 +Number RSCG: 9 - 1 [Breezy](/docs/Breezy) [![Nuget](https://img.shields.io/nuget/dt/Breezy.SourceGenerator?label=Breezy.SourceGenerator)](https://www.nuget.org/packages/Breezy.SourceGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Ludovicdln/Breezy?style=social) 2023-08-09 + 1 [AlephMapper](/docs/AlephMapper) [![Nuget](https://img.shields.io/nuget/dt/AlephMapper?label=AlephMapper)](https://www.nuget.org/packages/AlephMapper/) ![GitHub Repo stars](https://img.shields.io/github/stars/Raffinert/AlephMapper?style=social) 2026-06-29 - 2 [Dapper.AOT](/docs/Dapper.AOT) [![Nuget](https://img.shields.io/nuget/dt/Dapper.AOT?label=Dapper.AOT)](https://www.nuget.org/packages/Dapper.AOT/) ![GitHub Repo stars](https://img.shields.io/github/stars/DapperLib/DapperAOT?style=social) 2024-12-02 + 2 [Breezy](/docs/Breezy) [![Nuget](https://img.shields.io/nuget/dt/Breezy.SourceGenerator?label=Breezy.SourceGenerator)](https://www.nuget.org/packages/Breezy.SourceGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Ludovicdln/Breezy?style=social) 2023-08-09 - 3 [EntityLengths.Generator](/docs/EntityLengths.Generator) [![Nuget](https://img.shields.io/nuget/dt/EntityLengths.Generator?label=EntityLengths.Generator)](https://www.nuget.org/packages/EntityLengths.Generator/) ![GitHub Repo stars](https://img.shields.io/github/stars/TarasKovalenko/EntityLengths.Generator?style=social) 2025-02-19 + 3 [Dapper.AOT](/docs/Dapper.AOT) [![Nuget](https://img.shields.io/nuget/dt/Dapper.AOT?label=Dapper.AOT)](https://www.nuget.org/packages/Dapper.AOT/) ![GitHub Repo stars](https://img.shields.io/github/stars/DapperLib/DapperAOT?style=social) 2024-12-02 - 4 [Facet.Search](/docs/Facet.Search) [![Nuget](https://img.shields.io/nuget/dt/Facet.Search?label=Facet.Search)](https://www.nuget.org/packages/Facet.Search/) ![GitHub Repo stars](https://img.shields.io/github/stars/Tim-Maes/Facet.Search?style=social) 2025-12-17 + 4 [EntityLengths.Generator](/docs/EntityLengths.Generator) [![Nuget](https://img.shields.io/nuget/dt/EntityLengths.Generator?label=EntityLengths.Generator)](https://www.nuget.org/packages/EntityLengths.Generator/) ![GitHub Repo stars](https://img.shields.io/github/stars/TarasKovalenko/EntityLengths.Generator?style=social) 2025-02-19 - 5 [Finch.Generators](/docs/Finch.Generators) [![Nuget](https://img.shields.io/nuget/dt/Finch.Generators?label=Finch.Generators)](https://www.nuget.org/packages/Finch.Generators/) ![GitHub Repo stars](https://img.shields.io/github/stars/ivmazurenko/finch?style=social) 2025-08-10 + 5 [Facet.Search](/docs/Facet.Search) [![Nuget](https://img.shields.io/nuget/dt/Facet.Search?label=Facet.Search)](https://www.nuget.org/packages/Facet.Search/) ![GitHub Repo stars](https://img.shields.io/github/stars/Tim-Maes/Facet.Search?style=social) 2025-12-17 - 6 [Gedaq](/docs/Gedaq) [![Nuget](https://img.shields.io/nuget/dt/Gedaq?label=Gedaq)](https://www.nuget.org/packages/Gedaq/) ![GitHub Repo stars](https://img.shields.io/github/stars/SoftStoneDevelop/Gedaq?style=social) 2023-07-29 + 6 [Finch.Generators](/docs/Finch.Generators) [![Nuget](https://img.shields.io/nuget/dt/Finch.Generators?label=Finch.Generators)](https://www.nuget.org/packages/Finch.Generators/) ![GitHub Repo stars](https://img.shields.io/github/stars/ivmazurenko/finch?style=social) 2025-08-10 - 7 [TableStorage](/docs/TableStorage) [![Nuget](https://img.shields.io/nuget/dt/TableStorage?label=TableStorage)](https://www.nuget.org/packages/TableStorage/) ![GitHub Repo stars](https://img.shields.io/github/stars/StevenThuriot/TableStorage?style=social) 2024-06-01 + 7 [Gedaq](/docs/Gedaq) [![Nuget](https://img.shields.io/nuget/dt/Gedaq?label=Gedaq)](https://www.nuget.org/packages/Gedaq/) ![GitHub Repo stars](https://img.shields.io/github/stars/SoftStoneDevelop/Gedaq?style=social) 2023-07-29 - 8 [Unflat](/docs/Unflat) [![Nuget](https://img.shields.io/nuget/dt/Unflat?label=Unflat)](https://www.nuget.org/packages/Unflat/) ![GitHub Repo stars](https://img.shields.io/github/stars/pstlnce/unflat?style=social) 2025-08-18 + 8 [TableStorage](/docs/TableStorage) [![Nuget](https://img.shields.io/nuget/dt/TableStorage?label=TableStorage)](https://www.nuget.org/packages/TableStorage/) ![GitHub Repo stars](https://img.shields.io/github/stars/StevenThuriot/TableStorage?style=social) 2024-06-01 + + 9 [Unflat](/docs/Unflat) [![Nuget](https://img.shields.io/nuget/dt/Unflat?label=Unflat)](https://www.nuget.org/packages/Unflat/) ![GitHub Repo stars](https://img.shields.io/github/stars/pstlnce/unflat?style=social) 2025-08-18 \ No newline at end of file diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveDatabase.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveDatabase.mdx index 853348eb5..f769e62ec 100644 --- a/v2/rscg_examples_site/docs/Categories/_PrimitiveDatabase.mdx +++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveDatabase.mdx @@ -1,20 +1,22 @@ ### Category "Database" has the following generators: - 1 [Breezy](/docs/Breezy) [![Nuget](https://img.shields.io/nuget/dt/Breezy.SourceGenerator?label=Breezy.SourceGenerator)](https://www.nuget.org/packages/Breezy.SourceGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Ludovicdln/Breezy?style=social) 2023-08-09 + 1 [AlephMapper](/docs/AlephMapper) [![Nuget](https://img.shields.io/nuget/dt/AlephMapper?label=AlephMapper)](https://www.nuget.org/packages/AlephMapper/) ![GitHub Repo stars](https://img.shields.io/github/stars/Raffinert/AlephMapper?style=social) 2026-06-29 - 2 [Dapper.AOT](/docs/Dapper.AOT) [![Nuget](https://img.shields.io/nuget/dt/Dapper.AOT?label=Dapper.AOT)](https://www.nuget.org/packages/Dapper.AOT/) ![GitHub Repo stars](https://img.shields.io/github/stars/DapperLib/DapperAOT?style=social) 2024-12-02 + 2 [Breezy](/docs/Breezy) [![Nuget](https://img.shields.io/nuget/dt/Breezy.SourceGenerator?label=Breezy.SourceGenerator)](https://www.nuget.org/packages/Breezy.SourceGenerator/) ![GitHub Repo stars](https://img.shields.io/github/stars/Ludovicdln/Breezy?style=social) 2023-08-09 - 3 [EntityLengths.Generator](/docs/EntityLengths.Generator) [![Nuget](https://img.shields.io/nuget/dt/EntityLengths.Generator?label=EntityLengths.Generator)](https://www.nuget.org/packages/EntityLengths.Generator/) ![GitHub Repo stars](https://img.shields.io/github/stars/TarasKovalenko/EntityLengths.Generator?style=social) 2025-02-19 + 3 [Dapper.AOT](/docs/Dapper.AOT) [![Nuget](https://img.shields.io/nuget/dt/Dapper.AOT?label=Dapper.AOT)](https://www.nuget.org/packages/Dapper.AOT/) ![GitHub Repo stars](https://img.shields.io/github/stars/DapperLib/DapperAOT?style=social) 2024-12-02 - 4 [Facet.Search](/docs/Facet.Search) [![Nuget](https://img.shields.io/nuget/dt/Facet.Search?label=Facet.Search)](https://www.nuget.org/packages/Facet.Search/) ![GitHub Repo stars](https://img.shields.io/github/stars/Tim-Maes/Facet.Search?style=social) 2025-12-17 + 4 [EntityLengths.Generator](/docs/EntityLengths.Generator) [![Nuget](https://img.shields.io/nuget/dt/EntityLengths.Generator?label=EntityLengths.Generator)](https://www.nuget.org/packages/EntityLengths.Generator/) ![GitHub Repo stars](https://img.shields.io/github/stars/TarasKovalenko/EntityLengths.Generator?style=social) 2025-02-19 - 5 [Finch.Generators](/docs/Finch.Generators) [![Nuget](https://img.shields.io/nuget/dt/Finch.Generators?label=Finch.Generators)](https://www.nuget.org/packages/Finch.Generators/) ![GitHub Repo stars](https://img.shields.io/github/stars/ivmazurenko/finch?style=social) 2025-08-10 + 5 [Facet.Search](/docs/Facet.Search) [![Nuget](https://img.shields.io/nuget/dt/Facet.Search?label=Facet.Search)](https://www.nuget.org/packages/Facet.Search/) ![GitHub Repo stars](https://img.shields.io/github/stars/Tim-Maes/Facet.Search?style=social) 2025-12-17 - 6 [Gedaq](/docs/Gedaq) [![Nuget](https://img.shields.io/nuget/dt/Gedaq?label=Gedaq)](https://www.nuget.org/packages/Gedaq/) ![GitHub Repo stars](https://img.shields.io/github/stars/SoftStoneDevelop/Gedaq?style=social) 2023-07-29 + 6 [Finch.Generators](/docs/Finch.Generators) [![Nuget](https://img.shields.io/nuget/dt/Finch.Generators?label=Finch.Generators)](https://www.nuget.org/packages/Finch.Generators/) ![GitHub Repo stars](https://img.shields.io/github/stars/ivmazurenko/finch?style=social) 2025-08-10 - 7 [TableStorage](/docs/TableStorage) [![Nuget](https://img.shields.io/nuget/dt/TableStorage?label=TableStorage)](https://www.nuget.org/packages/TableStorage/) ![GitHub Repo stars](https://img.shields.io/github/stars/StevenThuriot/TableStorage?style=social) 2024-06-01 + 7 [Gedaq](/docs/Gedaq) [![Nuget](https://img.shields.io/nuget/dt/Gedaq?label=Gedaq)](https://www.nuget.org/packages/Gedaq/) ![GitHub Repo stars](https://img.shields.io/github/stars/SoftStoneDevelop/Gedaq?style=social) 2023-07-29 - 8 [Unflat](/docs/Unflat) [![Nuget](https://img.shields.io/nuget/dt/Unflat?label=Unflat)](https://www.nuget.org/packages/Unflat/) ![GitHub Repo stars](https://img.shields.io/github/stars/pstlnce/unflat?style=social) 2025-08-18 + 8 [TableStorage](/docs/TableStorage) [![Nuget](https://img.shields.io/nuget/dt/TableStorage?label=TableStorage)](https://www.nuget.org/packages/TableStorage/) ![GitHub Repo stars](https://img.shields.io/github/stars/StevenThuriot/TableStorage?style=social) 2024-06-01 + + 9 [Unflat](/docs/Unflat) [![Nuget](https://img.shields.io/nuget/dt/Unflat?label=Unflat)](https://www.nuget.org/packages/Unflat/) ![GitHub Repo stars](https://img.shields.io/github/stars/pstlnce/unflat?style=social) 2025-08-18 ### See category diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/AlephMapper.md b/v2/rscg_examples_site/docs/RSCG-Examples/AlephMapper.md new file mode 100644 index 000000000..3fb92e488 --- /dev/null +++ b/v2/rscg_examples_site/docs/RSCG-Examples/AlephMapper.md @@ -0,0 +1,620 @@ +--- +sidebar_position: 2720 +title: 272 - AlephMapper +description: AlephMapper helps you define LINQ-translatable object mappers for EF Core. +slug: /AlephMapper +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import TOCInline from '@theme/TOCInline'; +import SameCategory from '../Categories/_PrimitiveDatabase.mdx'; + +# AlephMapper by Yevhen Cherkes + + + + +## NuGet / site data +[![Nuget](https://img.shields.io/nuget/dt/AlephMapper?label=AlephMapper)](https://www.nuget.org/packages/AlephMapper/) +[![GitHub last commit](https://img.shields.io/github/last-commit/Raffinert/AlephMapper?label=updated)](https://github.com/Raffinert/AlephMapper) +![GitHub Repo stars](https://img.shields.io/github/stars/Raffinert/AlephMapper?style=social) + +## Details + +### Info +:::info + +Name: **AlephMapper** + +Source generator for creating projectable companions + +Author: Yevhen Cherkes + +NuGet: +*https://www.nuget.org/packages/AlephMapper/* + + +You can find more details at https://github.com/Raffinert/AlephMapper + +Source: https://github.com/Raffinert/AlephMapper + +::: + +### Author +:::note +Yevhen Cherkes +![Alt text](https://github.com/Raffinert.png) +::: + +## Original Readme +:::note + +[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://stand-with-ukraine.pp.ua) + +###### Terms of use[?](https://github.com/Tyrrrz/.github/blob/master/docs/why-so-political.md) + +By using this project or its source code, for any purpose and in any shape or form, you grant your **implicit agreement** to all the following statements: + +- You **condemn Russia and its military aggression against Ukraine** +- You **recognize that Russia is an occupant that unlawfully invaded a sovereign state** +- You **support Ukraine's territorial integrity, including its claims over temporarily occupied territories of Crimea and Donbas** +- You **reject false narratives perpetuated by Russian state propaganda** + +To learn more about the war and how you can help, [click here](https://stand-with-ukraine.pp.ua). Glory to Ukraine! + +### AlephMapper + +[![NuGet](https://img.shields.io/nuget/v/AlephMapper.svg)](https://www.nuget.org/packages/AlephMapper) +[![NuGet Downloads](https://img.shields.io/nuget/dt/AlephMapper.svg)](https://www.nuget.org/packages/AlephMapper) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Raffinert/AlephMapper/LICENSE) + +AlephMapper is a C# source generator for reusable manual mappings. Write one expression-bodied mapping method and generate companion methods for EF Core projections and optional update-in-place mapping from the same code. + +Use it when you want hand-written mapping logic without maintaining a separate `Expression>` for queries or a second method for updating existing objects. + +###### Features + +- **Single-source mappings** - use one mapping method for runtime mapping, query projection, and update variants. +- **EF Core-friendly projections** - generate `Expression>` methods for `.Select(...)`. +- **Method inlining** - reuse small helper methods in mappings and let AlephMapper inline them into generated code. +- **Configurable null handling** - choose how null-conditional access (`?.`) is handled in generated expressions. +- **Update-in-place mapping** - mutate existing destination instances, including EF Core tracked entities. + +###### Install + +Using the .NET CLI: + +```bash +dotnet add package AlephMapper +``` + +Using `PackageReference`: + +```xml + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +``` + +With Central Package Management: + +```xml + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +``` + +When referencing AlephMapper directly from source, add it as an analyzer-only project reference: + +```xml + +``` + +`PrivateAssets="all"` keeps AlephMapper from flowing transitively to consumers of your library, and `IncludeAssets` ensures the analyzer/source-generator assets are available at build time. + +###### Requirements + +- Mapping methods must be `static`, expression-bodied, and declared in a `static partial` class. + +###### Quick Start + +Add `using AlephMapper;`, then mark a mapping method or mapper class with `[Expressive]`. + +```csharp +using AlephMapper; + +public static partial class PersonMapper +{ + [Expressive] + public static PersonDto MapToPerson(Employee employee) => new() + { + Id = employee.EmployeeId, + FullName = GetFullName(employee), + Email = employee.ContactInfo.Email, + Department = employee.Department.Name + }; + + public static string GetFullName(Employee employee) => + employee.FirstName + " " + employee.LastName; +} +``` + +AlephMapper generates a projection companion method: + +```csharp +public static partial class PersonMapper +{ + public static Expression> MapToPersonExpression() => + employee => new PersonDto + { + Id = employee.EmployeeId, + FullName = employee.FirstName + " " + employee.LastName, + Email = employee.ContactInfo.Email, + Department = employee.Department.Name + }; +} +``` + +Use the generated expression in EF Core queries: + +```csharp +var people = await dbContext.Employees + .Select(PersonMapper.MapToPersonExpression()) + .ToListAsync(); +``` + +Use the original method for in-memory mapping: + +```csharp +var employee = GetEmployee(); +var dto = PersonMapper.MapToPerson(employee); +``` + +###### Null Handling + +C# null-conditional access (`?.`) is not directly supported in expression trees. AlephMapper lets you choose how to handle it. + +```csharp +[Expressive(NullConditionalRewrite = NullConditionalRewrite.Rewrite)] +public static partial class PersonMapper +{ + public static PersonSummary GetSummary(Person person) => new() + { + Name = person.Name, + City = person.Address?.City, + HasAddress = person.Address != null + }; +} +``` + +Available policies: + +| Policy | Behavior | +| --- | --- | +| `None` | Reports unsupported null-conditional usage. | +| `Ignore` | Removes null-conditional access, for example `person.Address?.City` becomes `person.Address.City`. | +| `Rewrite` | Emits explicit null checks, for example `person.Address != null ? person.Address.City : null`. | + +`Ignore` is the default. Use `Rewrite` when you want generated expressions to preserve null-safe behavior. + +###### Update Existing Objects + +Mark a mapping with `[Updatable]` to generate an overload that writes into an existing destination instance. + +```csharp +using AlephMapper; + +public static partial class PersonMapper +{ + [Updatable] + public static Person MapToPerson(PersonUpdateDto dto) => new() + { + FirstName = dto.FirstName, + LastName = dto.LastName, + Email = dto.Email + }; +} +``` + +Generated usage: + +```csharp +var person = await db.People.FindAsync(id); + +PersonMapper.MapToPerson(dto, target: person); + +await db.SaveChangesAsync(); +``` + +This is useful with EF Core change tracking because the tracked entity instance is preserved. + +Collection properties are skipped by default. To update collections, configure the policy: + +```csharp +[Updatable(CollectionProperties = CollectionPropertiesPolicy.Update)] +public static Person MapToPerson(PersonUpdateDto dto) => new() +{ + Orders = dto.Orders.Select(OrderMapper.MapToOrder).ToList() +}; +``` + +###### How It Works + +For each `[Expressive]` method, AlephMapper generates a method named `Expression()` returning `Expression>`. + +```csharp +public static PersonDto MapToPerson(Employee employee) => ... + +public static Expression> MapToPersonExpression() => ... +``` + +For each `[Updatable]` method, AlephMapper generates an overload with a `target` parameter. + +```csharp +public static PersonDto MapToPerson(Employee employee) => ... + +public static PersonDto MapToPerson(Employee employee, PersonDto target) => ... +``` + +Helper methods in the same mapper class are inlined where possible. + +###### Supported Mapping Shape + +Methods must be: + +- expression-bodied (`=>`) +- `static` +- declared in a `static partial` class +- visible to the source generator in the current compilation + +AlephMapper is best suited for object initializer mappings and small helper methods that can be inlined into generated expressions. + +###### Troubleshooting + +######### Generated method is missing + +Check that the mapper class is `static partial`, the method is expression-bodied, and the method or containing class has `[Expressive]` or `[Updatable]`. + +######### NullReferenceException after using `?.` + +The default null policy is `Ignore`, which removes null-conditional access in generated expressions. Use: + +```csharp +[Expressive(NullConditionalRewrite = NullConditionalRewrite.Rewrite)] +``` + +######### Updatable mapping is skipped for value types + +`[Updatable]` is intended for reference-type destinations. Value types are copied by value, so update-in-place semantics are not useful. + +######### Circular helper calls + +Generation is skipped when AlephMapper detects circular references between mapping/helper methods. Break the cycle or keep part of the logic outside the generated mapping path. + +######### Inspect generated code + +Add this to your project file: + +```xml + + true + +``` + +Generated files are emitted under the compiler-generated files output directory for your project. + +###### Comparison + +| Tool | Main style | AlephMapper difference | +| --- | --- | --- | +| AutoMapper | Runtime configuration and conventions | AlephMapper keeps mappings as ordinary C# methods. | +| Mapster | Convention/configuration mapping with code generation options | AlephMapper focuses on source-generating companions from manual mappings. | +| EntityFrameworkCore.Projectables | Projectable members for EF Core | AlephMapper targets mapping methods and update overloads. | +| Expressionify | Expression expansion | AlephMapper is mapping-oriented and also supports update-in-place generation. | + +###### Examples + +- [Sample app](https://github.com/Raffinert/AlephMapper/examples/SampleApp) - richer mapping examples. +- [Integration tests](https://github.com/Raffinert/AlephMapper/tests/AlephMapper.IntegrationTests) - EF Core and generated behavior coverage. + +###### Contributing + +Contributions are welcome. + +1. Fork the repository. +2. Create a feature branch. +3. Make the change. +4. Add or update tests. +5. Run the test suite. +6. Open a pull request. + +###### License + +This project is licensed under the MIT License. See [LICENSE](https://github.com/Raffinert/AlephMapper/LICENSE) for details. + +###### Acknowledgments + +- Inspired by [EntityFrameworkCore.Projectables](https://github.com/koenbeuk/EntityFrameworkCore.Projectables) and [Expressionify](https://github.com/ClaveConsulting/Expressionify). +- Thanks to all [contributors](https://github.com/Raffinert/AlephMapper/graphs/contributors). + +###### Related Projects + +- [EntityFrameworkCore.Projectables](https://github.com/koenbeuk/EntityFrameworkCore.Projectables) +- [Expressionify](https://github.com/ClaveConsulting/Expressionify) +- [AutoMapper](https://automapper.org/) +- [Mapster](https://github.com/MapsterMapper/Mapster) +- [Facet](https://github.com/Tim-Maes/Facet) + + +::: + +### About +:::note + +AlephMapper helps you define LINQ-translatable object mappers for EF Core. + + +It turns a normal C# mapping method into an expression that can be embedded in queries, so your entity-to-DTO projection can run inside the database query instead of after materialization. + + +This is useful for cleaner projection code, avoiding hand-written select expressions, and keeping mapping logic reusable across queries. + + +::: + +## How to use + +### Example (source csproj, source files) + + + + + +This is the CSharp Project that references **AlephMapper** +```xml showLineNumbers {10} + + + + Exe + net10.0 + enable + enable + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + true + $(BaseIntermediateOutputPath)\GX + + + +``` + + + + + + This is the use of **AlephMapper** in *Program.cs* + +```csharp showLineNumbers +using EntityDemo; + +Console.WriteLine("Hello, World!"); +DbContextOptionsBuilder optionsBuilder = new(); +//optionsBuilder.UseInMemoryDatabase("StatsDatabase"); +optionsBuilder.UseSqlite("Data Source=stats.db"); +var cnt = new DotNetStatsContext(optionsBuilder.Options); +await cnt.Database.EnsureCreatedAsync(); +Console.WriteLine("Database created"); + +var projDTO = cnt.Projects + .Select(ProjectExtensions.ProjToDTOExpression()); +Console.WriteLine(projDTO.ToQueryString()); +var result = await projDTO.ToArrayAsync(); + +Console.WriteLine(result.Length); +``` + + + + + This is the use of **AlephMapper** in *ProjectExtensions.cs* + +```csharp showLineNumbers +namespace EntityDemo; +public static partial class ProjectExtensions +{ + [AlephMapper.Expressive(NullConditionalRewrite = AlephMapper.NullConditionalRewrite.Rewrite)] + public static ProjectDTO ProjToDTO(Stats.Database.Project project) => + + new ProjectDTO + { + Name = project.Name, + CountStars = project.Stars?.Count ?? 0 + } + ; + +} +``` + + + + + This is the use of **AlephMapper** in *ProjectDTO.cs* + +```csharp showLineNumbers +namespace EntityDemo; + +public class ProjectDTO +{ + public string? Name \{ get; set; } + public int CountStars \{ get; set; } +} + +``` + + + + +### Generated Files + +Those are taken from $(BaseIntermediateOutputPath)\GX + + + + +```csharp showLineNumbers +using System; + +namespace AlephMapper; + +/// +/// Configures how null-conditional operators are handled +/// +public enum NullConditionalRewrite +{ + /// + /// Don't rewrite null conditional operators (Default behavior). + /// Usage of null conditional operators is thereby not allowed + /// + None, + + /// + /// Ignore null-conditional operators in the generated expression tree + /// + /// + /// (A?.B) is rewritten as expression: (A.B) + /// + Ignore, + + /// + /// Translates null-conditional operators into explicit null checks + /// + /// + /// (A?.B) is rewritten as expression: (A != null ? A.B : null) + /// + Rewrite +} + +/// +/// Marks a class to generate expressive companion methods. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class ExpressiveAttribute : Attribute +{ + /// + /// Get or set how null-conditional operators are handled + /// + public NullConditionalRewrite NullConditionalRewrite \{ get; set; \} = NullConditionalRewrite.Ignore; +} + +/// +/// Marks a class to generate update companion methods. +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +public sealed class UpdatableAttribute : Attribute +{ + /// + /// Gets or sets the policy for handling collection updates during mapping operations + /// + public CollectionPropertiesPolicy CollectionProperties \{ get; set; \} = CollectionPropertiesPolicy.Skip; +} + +/// +/// Defines the policy for handling collection updates during mapping operations +/// +public enum CollectionPropertiesPolicy +{ + /// + /// Skip collection updates - collections will not be modified during mapping + /// + Skip, + + /// + /// Update collections - collections will be updated during mapping operations + /// + Update +} +``` + + + + +```csharp showLineNumbers +using System; +using System.CodeDom.Compiler; +using System.Linq; +using System.Linq.Expressions; + +namespace EntityDemo; + +[GeneratedCode("AlephMapper", "0.5.5")] +partial class ProjectExtensions +{ + /// + /// This is an auto-generated expression companion for . + /// + /// + /// + /// Null handling strategy: Null-conditional operators are rewritten as explicit null checks for better compatibility. + /// + /// + public static Expression> ProjToDTOExpression() => + project => new ProjectDTO + { + Name = project.Name, + CountStars = (project.Stars != null + ? (project.Stars.Count) + : (int?)null) ?? 0 + }; +} + +``` + + + + +## Useful + +### Download Example (.NET C#) + +:::tip + +[Download Example project AlephMapper ](/sources/AlephMapper.zip) + +::: + + +### Share AlephMapper + + + +https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper + + + diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md index 0588215a3..9ee899b26 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: 271 RSCG list by category -description: 271 RSCG list by category +title: 272 RSCG list by category +description: 272 RSCG list by category slug: /rscg-examples --- @@ -393,7 +393,7 @@ import DocCardList from '@theme/DocCardList'; ## Database
- Expand Database =>examples:8 + Expand Database =>examples:9 @@ -434,6 +434,11 @@ import DocCardList from '@theme/DocCardList'; [Facet.Search](/docs/Facet.Search) + + + +[AlephMapper](/docs/AlephMapper) +
@@ -1825,6 +1830,8 @@ flowchart LR; Database--> Facet.Search((Facet.Search)) + Database--> AlephMapper((AlephMapper)) + Decorator--> DecoratorGenerator((DecoratorGenerator)) DependencyInjection--> AutoRegisterInject((AutoRegisterInject)) diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md index 527de206d..6c8054f46 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 271 Roslyn Source Code Generator (RSCG) +of 272 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 a6787b3a8..5ed0b81e4 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'; -## 271 RSCG with examples in descending chronological order +## 272 RSCG with examples in descending chronological order -This is the list of 271 ( 16 from Microsoft) RSCG with examples +This is the list of 272 ( 16 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 271 ( 16 from Microsoft) RSCG with examples | No | Name | Date | Category | | --------- | ----- | ---- | -------- | +|272| [AlephMapper by Yevhen Cherkes ](/docs/AlephMapper)|2026-06-29 => 29 June 2026 | [Database](/docs/Categories/Database) | |271| [TypedStateBuilder.Generator by Georgiy Petrov ](/docs/TypedStateBuilder.Generator)|2026-05-16 => 16 May 2026 | [Builder](/docs/Categories/Builder) | |270| [AssemblyMetadata.Generators by LoreSoft ](/docs/AssemblyMetadata.Generators)|2026-05-15 => 15 May 2026 | [EnhancementProject](/docs/Categories/EnhancementProject) | |269| [LinkDotNet.Enumeration by Steven Giesel ](/docs/LinkDotNet.Enumeration)|2026-05-14 => 14 May 2026 | [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 5d7af9dd0..23ee71dc2 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: '271 Examples (16 from MSFT)', +title: '272 Examples (16 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 23d74a2dd..e681a8851 100644 --- a/v2/rscg_examples_site/static/exports/RSCG.json +++ b/v2/rscg_examples_site/static/exports/RSCG.json @@ -2169,6 +2169,14 @@ "Source": "https://github.com/Georgiy-Petrov/TypedStateBuilder.Generator", "Category": "Builder", "AddedOn": "2026-05-16T00:00:00" + }, + { + "Name": "AlephMapper", + "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper", + "NuGet": "https://www.nuget.org/packages/AlephMapper/", + "Source": "https://github.com/Raffinert/AlephMapper", + "Category": "Database", + "AddedOn": "2026-06-29T00: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 b3f3492cd..07cb1215e 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/AlephMapper.zip b/v2/rscg_examples_site/static/sources/AlephMapper.zip new file mode 100644 index 000000000..f7f1cda82 Binary files /dev/null and b/v2/rscg_examples_site/static/sources/AlephMapper.zip differ