RSCG – AlephMapper
| name | AlephMapper |
| nuget | https://www.nuget.org/packages/AlephMapper/ |
| link | https://github.com/Raffinert/AlephMapper |
| author | Yevhen Cherkes |
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.
This is how you can use AlephMapper .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AlephMapper" Version="0.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
The code that you will use is
using EntityDemo;
Console.WriteLine("Hello, World!");
DbContextOptionsBuilder<DotNetStatsContext> 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);
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
}
;
}
namespace EntityDemo;
public class ProjectDTO
{
public string? Name { get; set; }
public int CountStars { get; set; }
}
The code that is generated is
using System;
namespace AlephMapper;
/// <summary>
/// Configures how null-conditional operators are handled
/// </summary>
public enum NullConditionalRewrite
{
/// <summary>
/// Don't rewrite null conditional operators (Default behavior).
/// Usage of null conditional operators is thereby not allowed
/// </summary>
None,
/// <summary>
/// Ignore null-conditional operators in the generated expression tree
/// </summary>
/// <remarks>
/// <c>(A?.B)</c> is rewritten as expression: <c>(A.B)</c>
/// </remarks>
Ignore,
/// <summary>
/// Translates null-conditional operators into explicit null checks
/// </summary>
/// <remarks>
/// <c>(A?.B)</c> is rewritten as expression: <c>(A != null ? A.B : null)</c>
/// </remarks>
Rewrite
}
/// <summary>
/// Marks a class to generate expressive companion methods.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class ExpressiveAttribute : Attribute
{
/// <summary>
/// Get or set how null-conditional operators are handled
/// </summary>
public NullConditionalRewrite NullConditionalRewrite { get; set; } = NullConditionalRewrite.Ignore;
}
/// <summary>
/// Marks a class to generate update companion methods.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class UpdatableAttribute : Attribute
{
/// <summary>
/// Gets or sets the policy for handling collection updates during mapping operations
/// </summary>
public CollectionPropertiesPolicy CollectionProperties { get; set; } = CollectionPropertiesPolicy.Skip;
}
/// <summary>
/// Defines the policy for handling collection updates during mapping operations
/// </summary>
public enum CollectionPropertiesPolicy
{
/// <summary>
/// Skip collection updates - collections will not be modified during mapping
/// </summary>
Skip,
/// <summary>
/// Update collections - collections will be updated during mapping operations
/// </summary>
Update
}
using System;
using System.CodeDom.Compiler;
using System.Linq;
using System.Linq.Expressions;
namespace EntityDemo;
[GeneratedCode("AlephMapper", "0.5.5")]
partial class ProjectExtensions
{
/// <summary>
/// This is an auto-generated expression companion for <see cref="ProjToDTO(Project)"/>.
/// </summary>
/// <remarks>
/// <para>
/// Null handling strategy: Null-conditional operators are rewritten as explicit null checks for better compatibility.
/// </para>
/// </remarks>
public static Expression<Func<Project, ProjectDTO>> ProjToDTOExpression() =>
project => new ProjectDTO
{
Name = project.Name,
CountStars = (project.Stars != null
? (project.Stars.Count)
: (int?)null) ?? 0
};
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/AlephMapper