EFQueryMapper 2.0.0

dotnet add package EFQueryMapper --version 2.0.0                
NuGet\Install-Package EFQueryMapper -Version 2.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EFQueryMapper" Version="2.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add EFQueryMapper --version 2.0.0                
#r "nuget: EFQueryMapper, 2.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install EFQueryMapper as a Cake Addin
#addin nuget:?package=EFQueryMapper&version=2.0.0

// Install EFQueryMapper as a Cake Tool
#tool nuget:?package=EFQueryMapper&version=2.0.0                

EFQueryMapper

EFQueryMapper is a powerful library for type mapping, including support for Entity Framework's IQueryable interface. It allows for efficient mapping between entities and DTOs, streamlining the data transformation process in .NET applications. Mapping profiles are created only once and saved, which makes subsequent mapping operations very fast. If there is no need for any configuration between two types, you don't need to do anything in mapper. Not even a 'CreateMap' required if no configuration needed.

Why use it over other mapping libraries?

  • Entity Framework query mapping on the fly! Thanks to Expression API 🥂
  • High performance by caching mapping expressions
  • Provides customization abilities in a simple way
  • Lets you see mapping expressions between types (visit example at bottom)
  • Easy to use with the help of extension methods
  • Works with not only entity framework queries via IQueryable interface, also any collection type and class can be mapped.

Example

You can map directly using IQueryable interface. Here's a quick example:

IQueryable<Person> peopleQuery = dbcontext.Person.Where(x=> x.Age >= 18);
List<PersonDTO> extensionQueryPeople = peopleQuery.Map<PersonDTO>(mapper).ToList();

Or you can go this way:

List<PersonDTO> value = mapper.Map<Person, PersonDTO>(peopleQuery).ToList();

See also another extension example with Map method:

 public virtual async Task<TEntity> Create<TEntity, TRequest>(TRequest request, bool save = true)
     where TRequest : class
     where TEntity : class
 {
     var entity = request.Map<TEntity>(queryMapper);
     .....
 }

Installation

You can install the package via NuGet Package Manager:

Install-Package EFQueryMapper

Or via .NET CLI:

dotnet add package EFQueryMapper

Usage

You could add it to DI this way:

 services.AddQueryMapper<BookMapper>()

The mapper is registered as a singleton, allowing you to inject it wherever needed. For example, see the code below:

 public BookRepository(BookContext bookContext, IQueryMapper queryMapper)
 {
     BookContext = bookContext;
     QueryMapper = queryMapper;
 }

Your custom mapper must be a subclass of QueryMapper. It is a MUST since QueryMapper itself already covers all the needed stuff and implements IQueryMapper readily with a flexible way. Let's take a look at an example:

public class BookMapper : QueryMapper
{
    public BookMapper()
    {
        Configure<Person, PersonDTO>(config =>
        {
            config
            .Match(x => x.Firstname + " " + x.Lastname, dto => dto.Fullname)
            .UsingPublicConstructor(x => new PersonDTO(x.Firstname, x.Lastname))
            ;
        });

        Configure<Book, ReadBookResponse>(config =>
        {
            config
            .Match(x => x.Author.FirstName + " " + x.Author.LastName, y => y.AuthorName)
            .Match(x => x.CreatedBy.FirstName + " " + x.CreatedBy.LastName, y => y.CreatedByName)
            .UsingNonPublicConstructor(x => new ParameterContainer(x.AuthorId))
            ;
        });

        Configure<Note, ReadNoteResponse>(config =>
        {
            config
            .Match(x => x.User.FirstName + " " + x.User.LastName, y => y.UserName)
            .Match(x => x.User.ShareId, y => y.ShareId)
            ;
        });
    }
}

Changed this way starting from version 2.0.0:

// Starting from version 2.0.0
public class BookMapper : QueryMapper
{
  
    protected override void Configure(ConfigurationBuilder builder)
    {
        builder.Configure<Person, PersonDTO>(config =>
        {
            config
            .Match(x => x.Firstname + " " + x.Lastname, dto => dto.Fullname)
            .UsingPublicConstructor(x => new PersonDTO(x.Firstname, x.Lastname))
            ;
        });

        builder.Configure<Book, ReadBookResponse>(config =>
        {
            config
            .Match(x => x.Author.FirstName + " " + x.Author.LastName, y => y.AuthorName)
            .Match(x => x.CreatedBy.FirstName + " " + x.CreatedBy.LastName, y => y.CreatedByName)
            .UsingNonPublicConstructor(x => new ParameterContainer(x.AuthorId))
            ;
        });

        builder.Configure<Note, ReadNoteResponse>(config =>
        {
            config
            .Match(x => x.User.FirstName + " " + x.User.LastName, y => y.UserName)
            .Match(x => x.User.ShareId, y => y.ShareId)
            ;
        });
    }
}

You see we have "Configure" method. It has a couple methods to extend mapping abilities:

Match: Allows you to determine source type expression to use and the destination type's property/field to set. See below example:

.Match(x => x.User.ShareId, y => y.ShareId)

UsingPublicConstructor: Allows you to use the public constructor you wish. If destination class has multiple constructors and you want to pick a certain one, this method would be useful. If you don't use and map straight, first constructor found will be used. See below example:

.UsingPublicConstructor(x => new PersonDTO(x.Firstname, x.Lastname))

Public constructors are prioritized while picking up constructor automatically.

Lets say you have only one constructor and it has parameters. You don't need anything if parameter names are matched to related property/field. Imagine you have only this constructor:

 private ReadBookResponse(int authorId)
 {
     AuthorId = authorId;
 }

Even if it is private, it is easily used with mapper. If you have multiple constructors, public ones have priority to be picked up by mapper. Since member names are matched, you don't have to make any configurations. If parameter namings don't match just use the method above.

If you need to use a non-public constructor of destination type and there are multiple constructors, see below example:

// use 'builder.Configure' starting from version 2.0.0 inside the method with signature:
// protected override void Configure(ConfigurationBuilder builder)

  Configure<Book, ReadBookResponse>(config =>
  {
      config
      .Match(x => x.Author.FirstName + " " + x.Author.LastName, y => y.AuthorName)
      .Match(x => x.CreatedBy.FirstName + " " + x.CreatedBy.LastName, y => y.CreatedByName)
      .UsingNonPublicConstructor(x => new ParameterContainer(x.AuthorId))
      ;
  });

You could also check the mapping expression between types:

// Could be used starting from version 2.0.0
 public string GetExpression() =>
      QueryMapper.GetMappingExpression<Book, ReadBookResponse>();

In UsingNonPublicConstructor you could add arguments you wish, ensure the argument types you added to ParameterContainer match the wished destination type constructor's argument types you want to use.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on EFQueryMapper:

Package Downloads
QueryBase

This library provides a powerful generic repository base, leveraging the capabilities of Entity Framework and QueryMapper. It simplifies CRUD operations, making them easily extendable. Pagination, filtering, ordering, and bulk operations are all implemented in a generic and flexible way. Most methods can be overridden for customization.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.0 86 12/1/2024
1.0.5 125 10/20/2024
1.0.4 91 10/15/2024
1.0.3 94 10/9/2024
1.0.2 85 10/7/2024
1.0.1 92 10/7/2024
1.0.0 96 10/2/2024

now it is possible to check mapping expressions between types. The mapper should use the parameterless constructor. It means mapper should have no dependency naturally