Weequery.AutoMapper 3.1.0

dotnet add package Weequery.AutoMapper --version 3.1.0
                    
NuGet\Install-Package Weequery.AutoMapper -Version 3.1.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="Weequery.AutoMapper" Version="3.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Weequery.AutoMapper" Version="3.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Weequery.AutoMapper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Weequery.AutoMapper --version 3.1.0
                    
#r "nuget: Weequery.AutoMapper, 3.1.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.
#:package Weequery.AutoMapper@3.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Weequery.AutoMapper&version=3.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Weequery.AutoMapper&version=3.1.0
                    
Install as a Cake Tool

Weequery.AutoMapper

AutoMapper support for Weequery. I decide which rows. AutoMapper decides what a row looks like. A tidy division of labour, and the only one in this repository that comes with a lawyer.

var (page, total) = context.Minions
    .WithWeequery()
    .BindProperties(MinionBindings)
    .ApplyCondition(request.Filter)
    .ApplySorts(request.Sort, DefaultSort)
    .ApplyPagination(request.PageSize, request.Page)
    .ProjectToPaged<Minion, MinionSummary>(configuration);

var matched = await total.FirstOrDefaultAsync();
var rows    = await page.ToListAsync();

Only the columns the DTO needs ever leave the database:

SELECT "m"."Name", "m"."Alias", "m"."Pay"
FROM "Minions" AS "m"
WHERE "m"."IsActive"

AutoMapper is dual licensed, and taking this package means taking that licence. Read this before you install anything. I am telling you now, at the top, in a box, because I refuse to have this conversation later.

AutoMapper was MIT through 14.x. From 15 it is free for individuals, for non commercial use and for organisations under a revenue threshold, and asks everyone above that line to register a paid key at automapper.io. Whether you are above the line is yours to establish. It is not this README's business to guess at your revenue, however curious I may be.

This package references 15.1.3, and that is the version with the licence. It used to reference 14.0.0 to stay MIT, which cost it CVE-2026-32933: an unauthenticated denial of service through uncontrolled recursion on deeply nested object graphs, CVSS 7.5, StackOverflowException, process termination. It affects every MIT release and the MIT line will never be patched, so there is no version of this dependency that is both MIT and safe. Buying the patch is what the licence is for. I am in the denial-of-service business and even I will not ship you one by accident.

It is a minimum, not a pin, so a project already on 16 resolves to 16 and nothing here needs touching. 15.1.3 is the lowest patched version of the lowest licensed major, which keeps the floor as low as it can honestly go, and I do mean honestly.

If the licence does not suit you, Weequery.Mapster and Weequery.Mapperly do precisely the same job against MIT mappers, and I will not be offended.

I have no dependencies myself, and this package is separate so that installing me never drags AutoMapper in behind you.

If you would rather stay on 14

You can. It costs two lines and a small amount of dignity. 14.0.0 is the last MIT release, and everything I use is present from 14 onwards, so nothing here stops working:

<PropertyGroup>
  <NoWarn>NU1605</NoWarn>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="Weequery.AutoMapper" Version="3.0.0" />
  <PackageReference Include="AutoMapper" Version="14.0.0" />
</ItemGroup>

The second reference is what picks the version: one you declare yourself beats the one a package you depend on asks for. The NoWarn is not decoration. Without it, restore fails, exit code and all:

error NU1605: Detected package downgrade: AutoMapper from 15.1.3 to 14.0.0

That is NuGet's own default in current SDKs rather than anything I chose, and for once I agree with it entirely: a dependency stepping backwards over a security floor ought to cost somebody a deliberate line of configuration. NU1903 then stays on for every restore, reminding you about the CVE. Leave it on. Once you have done this, that warning is the only thing left in the building that knows which version you are on.

And no, the floor cannot be 14 while 15 stays the default. A Version is a minimum, and NuGet installs the lowest version that satisfies it rather than the newest available, so the floor and the default are the same number. A floor of 14 would put every consumer on 14, which is exactly where this package started and exactly why it moved.

What it adds

ProjectTo<T, TDto>(configuration) Build(), then AutoMapper's own ProjectTo. Returns IQueryable<TDto>
ProjectToPaged<T, TDto>(configuration) BuildPaged(), with only the page projected. Returns PagedQuery<TDto>

Both will also take an IMapper, for the caller who has that injected rather than the configuration. I am accommodating.

The paged one is the reason this package exists. The unpaged case is already a one-liner without me, inquiry.Build().ProjectTo<MinionSummary>(configuration), and needs only one type argument, because C# infers all of a method's type arguments or none of them. The paged case has no such shortcut and exactly one detail that is easy to get wrong.

Only the page is projected

PagedQuery<TDto> holds an IQueryable<TDto> page and an IQueryable<int> count, and that asymmetry is the whole point. A count is of rows, not of what you read off them, so it asks the database for a number and reads no column at all. Nothing of the mapping appears in that statement, so a DTO holding something AutoMapper will not project cannot fail a count that never needed it.

Read the number with FirstOrDefaultAsync. CountAsync compiles and answers 1, because it counts the rows of the count query rather than the rows counted. The core README puts the three endings side by side, where you may study them at your leisure.

Neither query has run, exactly as BuildPaged promised.

Order of operations

Filter, sort, window, then project.

Paging is over the entity's own sort, and projecting first would leave AutoMapper's output to be sorted by names the DTO may not even have. My order is also what lets a sort name a field the DTO does not expose at all:

.ApplySorts([new Sort("IsActive", SortDirection.Ascending)])   // IsActive is nowhere on MinionSummary
.ProjectTo<Minion, MinionSummary>(configuration)

The allow-list is unchanged by any of it. Conditions and sorts still name binding keys on the entity, and a field nobody bound is refused exactly as it is anywhere else. The DTO's property names have nothing to do with this and never did.

One thing it refuses

ApplyProjection and a DTO are two answers to one question. One says a row is the keys the caller named. The other says a row is the DTO. Doing both would honour the DTO and drop the projection without a word, which for a caller whose projection arrived in a request is a filter's worth of intention quietly discarded. So I refuse:

ProjectTo cannot be used on an Inquiry that has already had ApplyProjection('[Name], [Pay]') called on it:
the DTO decides what a row holds here, so the projected fields would be silently ignored. Drop one of the two

Use BuildProjected() for caller-chosen columns, or a DTO for a fixed shape. Not both. I will not be party to both.

Versions

Built and tested against AutoMapper 15.1.3. The ProjectTo surface I use is stable across 14, 15 and 16, so referencing a newer one in your own project works without a thing changing here.

What is not stable is MapperConfiguration's own constructor: 14 took the configuring action, 15 and later take it alongside an ILoggerFactory. So the code that builds your configuration is yours to keep in step, not mine:

new MapperConfiguration(config => config.CreateMap<Minion, MinionSummary>(), loggerFactory);

That single line is the whole of what moving off 14 costs you. I have seen people pay more for less.

Licence

MIT, same as me. The AutoMapper dependency is emphatically not, see the box above, which I put there for a reason.

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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.1.0 40 9/24/2026
3.0.0 41 9/22/2026

No changes of its own. Released alongside Weequery 3.1.0, and requires it.