LuckyPennySoftware/AutoMapper · error · AutoMapperConfigurationException

CreateProjection works with ProjectTo, not with Map.

Error message

CreateProjection works with ProjectTo, not with Map.

What it means

A map created with CreateProjection<TSrc,TDest> has TypeMap.Projection = true and is compiled only as a LINQ expression for ProjectTo; it has no runtime Map strategy. TypeMap.CheckProjection throws AutoMapperConfigurationException (with the missing-map inner exception) the moment such a map is selected for an in-memory Map. The message states the rule directly.

Source

Thrown at src/AutoMapper/TypeMap.cs:57

            }
            sourceMembers.Clear();
            var propertyType = destinationProperty.GetMemberType();
            if (profile.MapDestinationPropertyToSource(SourceTypeDetails, destinationType, propertyType, destinationName, sourceMembers, isReverseMap))
            {
                AddPropertyMap(destinationProperty, propertyType, sourceMembers);
            }
        }
    }
    public string CheckRecord() => ConstructorMap?.Ctor is ConstructorInfo ctor && ctor.IsFamily && ctor.Has<CompilerGeneratedAttribute>() ?
        " When mapping to records, consider using only public constructors. See https://docs.automapper.io/en/latest/Construction.html." : null;
    public Features<IRuntimeFeature> Features => Details.Features;
    private TypeMapDetails Details => _details ??= new();
    public bool HasDetails => _details != null;
    public void CheckProjection()
    {
        if (Projection)
        {
            throw new AutoMapperConfigurationException("CreateProjection works with ProjectTo, not with Map.", MissingMapException(Types));
        }
    }
    public static Exception MissingMapException(TypePair types) => MissingMapException(types.SourceType, types.DestinationType);
    public static Exception MissingMapException(Type sourceType, Type destinationType)
        => new InvalidOperationException($"Missing map from {sourceType} to {destinationType}. Create using CreateMap<{sourceType.Name}, {destinationType.Name}>.");
    public bool Projection { get; set; }
    public LambdaExpression MapExpression { get; private set; }
    public Expression Invoke(Expression source, Expression destination) =>
        Expression.Invoke(MapExpression, ToType(source, SourceType), ToType(destination, DestinationType), ContextParameter);
    internal bool CanConstructorMap() => Profile.ConstructorMappingEnabled && !DestinationType.IsAbstract &&
        !CustomConstruction && !HasTypeConverter && DestinationConstructors.Length > 0;
    public TypePair Types;
    public ConstructorMap ConstructorMap { get; set; }
    public TypeDetails SourceTypeDetails { get; private set; }
    public TypeDetails DestinationTypeDetails { get; private set; }
    public Type SourceType => Types.SourceType;
    public Type DestinationType => Types.DestinationType;
    public ProfileMap Profile { get; }

View on GitHub (pinned to dfa6dd587c)

Solutions

  1. Change CreateProjection<TSrc,TDest> to CreateMap<TSrc,TDest> (or add a separate CreateMap) when in-memory Map is also needed.
  2. Keep the projection for EF queries and use ProjectTo exclusively for that DTO.
  3. Split into two DTOs/maps: a query projection DTO (ProjectTo) and a command DTO (CreateMap).
  4. If inheritance is involved, make the base map a CreateMap, not a CreateProjection.

Example fix

// before
var config = new MapperConfiguration(c => c.CreateProjection<Order, OrderDto>());
var dto = mapper.Map<OrderDto>(order); // throws: projection not for Map

// after
var config = new MapperConfiguration(c => c.CreateMap<Order, OrderDto>());
var dto = mapper.Map<OrderDto>(order);
Defensive patterns

Strategy: validation

Validate before calling

var typeMap = mapper.ConfigurationProvider.FindTypeMapFor<TSource, TDestination>();
if (typeMap is { Projection: true })
{
    throw new InvalidOperationException(
        $"{typeof(TSource)}->{typeof(TDestination)} is a projection; use ProjectTo, or switch to CreateMap.");
}

var dest = mapper.Map<TDestination>(source);

Type guard

static bool IsInMemoryMappable(IConfigurationProvider cfg, Type src, Type dst) =>
    cfg.FindTypeMapFor(src, dst) is { } tm && !tm.Projection;

Try / catch

try { dest = mapper.Map<TDestination>(source); }
catch (AutoMapperConfigurationException ex) when (ex.Message.Contains("CreateProjection works with ProjectTo"))
{
    // switch the map to CreateMap, or call ProjectTo instead, then retry
}

Prevention

When it happens

Trigger: cfg.CreateProjection<A,B>() followed by mapper.Map<B>(anA). Also when an inheritance chain or Include pulls a projection-only map into an in-memory mapping path.

Common situations: Reusing a query/read-model DTO for command-side in-memory mapping; copy-pasting CreateProjection where CreateMap was intended; mixing ProjectTo query DTOs with Map-based handlers; inheritance base defined as a projection.

Related errors


AI-assisted analysis of LuckyPennySoftware/AutoMapper@dfa6dd587c (2026-08-13). Data as JSON: /api/errors/de0a2141aba1b1ca. Report an issue: GitHub.