Automapper
Map Method
Programming
Parameter Passing
C#

Automapper passing parameter to Map method

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

AutoMapper configuration is usually static, but some mapped fields depend on runtime values such as locale, currency, current user, or tenant. The correct way to pass that runtime data is through mapping options at call time, not by mutating global mapper configuration.

In practice, that means using the opts.Items dictionary when calling Map, then reading those values from the ResolutionContext inside a resolver or mapping expression. This keeps the profile stable and the request-specific data local to one mapping operation.

Why Runtime Parameters Need Special Handling

Profiles are built once and then reused. If you try to inject request-specific values directly into profile setup, you create hidden global state and cross-request bugs.

A safer pattern is:

  • keep the profile static
  • pass values at map time
  • read them from the mapping context

That preserves the intended startup-time configuration model of AutoMapper.

Basic opts.Items Pattern

Suppose you want to format a total using runtime currency and culture information.

csharp
1public sealed class Order
2{
3    public int Id { get; set; }
4    public decimal Total { get; set; }
5}
6
7public sealed class OrderDto
8{
9    public int Id { get; set; }
10    public string DisplayTotal { get; set; } = string.Empty;
11}

Create a resolver that reads context items.

csharp
1using AutoMapper;
2using System.Globalization;
3
4public sealed class DisplayTotalResolver : IValueResolver<Order, OrderDto, string>
5{
6    public string Resolve(Order source, OrderDto destination, string destMember, ResolutionContext context)
7    {
8        var currency = context.Items.TryGetValue("currency", out var c)
9            ? c?.ToString() ?? "USD"
10            : "USD";
11
12        var culture = context.Items.TryGetValue("culture", out var cul)
13            ? CultureInfo.GetCultureInfo(cul?.ToString() ?? "en-US")
14            : CultureInfo.GetCultureInfo("en-US");
15
16        return string.Format(culture, "{0} {1:N2}", currency, source.Total);
17    }
18}

Profile setup stays static.

csharp
1using AutoMapper;
2
3public sealed class MappingProfile : Profile
4{
5    public MappingProfile()
6    {
7        CreateMap<Order, OrderDto>()
8            .ForMember(d => d.DisplayTotal, opt => opt.MapFrom<DisplayTotalResolver>());
9    }
10}

Then pass the values at map time.

csharp
1Order order = new Order { Id = 42, Total = 1530.75m };
2
3OrderDto dto = mapper.Map<OrderDto>(order, opts =>
4{
5    opts.Items["currency"] = "CAD";
6    opts.Items["culture"] = "en-CA";
7});

Keep the Scope Narrow

Items is useful for small per-call values, but it should not become a dumping ground for large service objects or unrelated business state.

A good rule is:

  • lightweight projection data belongs in Items
  • domain decisions should already be made before mapping begins

If a resolver needs many unrelated values, the mapping layer may be doing too much work.

Testing Runtime Parameter Behavior

These mappings are easy to unit test because the runtime inputs are explicit.

csharp
1[Fact]
2public void Maps_DisplayTotal_With_Provided_Currency()
3{
4    var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingProfile>());
5    var mapper = config.CreateMapper();
6
7    var src = new Order { Id = 1, Total = 10m };
8    var dto = mapper.Map<OrderDto>(src, opts => opts.Items["currency"] = "EUR");
9
10    Assert.Contains("EUR", dto.DisplayTotal);
11}

It is also worth testing the default path where the item is missing, so the fallback behavior is deliberate rather than accidental.

Projection Caveat

A useful limitation to remember: runtime Items works for in-memory mapping, but expression-based projections such as ProjectTo have different constraints because they are translated into query expressions. If the formatting depends on request-specific runtime values, it is often better to project the raw data first and format it afterward.

Common Pitfalls

A common mistake is trying to store request-specific state in static mapping configuration. That works poorly under concurrency and is conceptually the wrong layer.

Another issue is using inconsistent key names in Items, which turns the mapping call into a stringly typed contract with no discipline.

Developers also sometimes pass whole service objects through Items instead of small values. That makes mappings harder to understand and harder to test.

Finally, if a mapping resolver contains lots of business logic, the real problem is usually not parameter passing. The real problem is misplaced logic.

Summary

  • Pass runtime mapping parameters through opts.Items when calling Map.
  • Read those values through ResolutionContext.Items inside resolvers.
  • Keep AutoMapper profiles static and request-agnostic.
  • Use Items for small projection concerns, not as a service container.
  • Test both explicit and default parameter paths so mapping behavior stays predictable.

Course illustration
Course illustration

All Rights Reserved.