Skip to main content

Map / MapAsync

Map transforms the success value of a Result without changing whether it succeeded or failed. The transformation cannot fail — if it can, use Then instead.

Signatures

// Sync
Result<TOut> Map<TIn, TOut>(this Result<TIn> result, Func<TIn, TOut> transform)

// Async
Task<Result<TOut>> MapAsync<TIn, TOut>(this Result<TIn> result, Func<TIn, Task<TOut>> transform)
Task<Result<TOut>> MapAsync<TIn, TOut>(this Task<Result<TIn>> resultTask, Func<TIn, TOut> transform)
Task<Result<TOut>> MapAsync<TIn, TOut>(this Task<Result<TIn>> resultTask, Func<TIn, Task<TOut>> transform)

When to use

Use Map for transformations that always succeed:

  • Converting an entity to a DTO
  • Extracting a property from an object
  • Formatting a string
  • Any pure computation

Do not use Map when the operation can fail — use Then. Do not use Map for side effects — use Tap.

Basic example

public Result<UserDto> GetUserDto(int userId)
{
return GetUser(userId)
.Map(user => new UserDto
{
Id = user.Id,
Name = user.Name,
Email = user.Email
});
}

If GetUser fails, the Map is skipped and the original error passes through.

Extracting a property

Result<string> GetUserEmail(int userId)
{
return GetUser(userId)
.Map(user => user.Email);
}

Chaining maps

var result = Result.Start("  Hello, World!  ")
.Map(s => s.Trim())
.Map(s => s.ToUpperInvariant())
.Map(s => s.Length);

// result.Value == 13

Async map

var result = await GetUserAsync(userId)
.MapAsync(async user => await _avatarService.GetUrlAsync(user.Id));

Map vs Then

MapThen
Transformation can fail?NoYes
Return type of lambdaTOutResult<TOut>
Use forDTOs, formatting, projectionsDB queries, API calls, validation
// Map — transformation always succeeds
.Map(user => user.ToDto())

// Then — operation can fail
.Then(user => ValidateAge(user))

See also

  • Then — chain fallible operations
  • Validate — assert conditions
  • Tap — side effects