LyraLearn AI Learning Platform
Exams
← Module 6 Β· Layered Architecture
🎧 Listen

AutoMapper Without Tears

Layered apps map constantly β€” entity to view model, view model to DTO β€” and AutoMapper exists to kill that boilerplate. Used with discipline it's a quiet workhorse; used carelessly it's a famous source of runtime surprises. The difference is three habits.

Profiles: mapping as configuration

Mappings live in Profile classes, grouped by feature, registered once at startup:

public class PermitProfile : Profile
{
    public PermitProfile()
    {
        CreateMap<Permit, PermitRowVm>()
            .ForMember(d => d.StatusName, o => o.MapFrom(s => s.Status.Name));
    }
}

By convention, same-named properties map automatically; ForMember handles the rest. Flattening is built in too β€” a destination ApplicantName picks up source.Applicant.Name without configuration. Keep profiles near the feature they serve, and keep them boring: heavy MapFrom lambdas full of conditionals are business logic hiding in mapping config, where nobody will look for it.

ProjectTo: mapping inside the SQL

The underrated feature. Map runs on objects already in memory; ProjectTo<T> translates the mapping into the LINQ projection itself:

var rows = _db.Permits
    .ProjectTo<PermitRowVm>(_mapper.ConfigurationProvider)
    .ToDataSourceResult(request);

EF generates a SELECT of exactly the mapped columns β€” related data becomes joins, not lazy loads. This combines everything Module 5 preached (project early, avoid N+1, no tracking overhead) with zero hand-written projections. For grid read actions, ProjectTo + ToDataSourceResult is the canonical pairing. Limitation: only expression-translatable mappings work β€” no method calls SQL can't understand β€” which is a feature, since it keeps mapping config translatable and simple.

When explicit mapping beats magic

AutoMapper's failure mode is invisibility: rename an entity property and the mapping silently stops populating a field β€” no compile error, just a blank column a user notices three weeks later. So don't use it everywhere. Write explicit mapping code (plain constructors or extension methods) when the transformation has real logic in it, when the shapes barely align, or when the object is critical enough that you want the compiler enforcing every assignment. A reasonable team rule: AutoMapper for wide, mechanical, property-for-property mappings (grids, lists, DTOs); explicit code for anything with decisions in it.

Test the configuration

The silent-breakage risk has a cheap antidote β€” one unit test that runs AssertConfigurationIsValid() against the full mapper configuration. It fails if any destination property on any map has no source, catching renames and forgotten ForMembers at build time instead of in production. Pair it with Ignore() for deliberately unmapped members so the assertion stays meaningful. If your project uses AutoMapper and doesn't have this test, adding it is a genuinely valuable first-week pull request.

🧠 Quiz yourself on this lesson →

Ask the AI Tutor

Grounded in the course lessons β€” it cites its sources and says when it doesn't know.