AlephMapper

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


Posted

in

,

by

Tags: