使用 AutoMapper 测试 .NET6 最小 API

Test .NET6 minimal API with AutoMapper

我有一个包含最少 API 的 .NET6 项目。这是代码

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<ClientContext>(opt => 
  opt.UseInMemoryDatabase("Clients"));
builder.Services
  .AddTransient<IClientRepository,
                ClientRepository>();
builder.Services
  .AddAutoMapper(Assembly.GetEntryAssembly());

var app = builder.Build();

// Get the Automapper, we can share this too
var mapper = app.Services.GetService<IMapper>();
if (mapper == null)
{
    throw new InvalidOperationException(
      "Mapper not found");
}

app.MapPost("/clients",
  async (ClientModel model,
         IClientRepository repo) =>
  {
      try
      {
          var newClient = mapper.Map<Client>(model);
          repo.Add(newClient);
          if (await repo.SaveAll())
          {
              return Results.Created(
                $"/clients/{newClient.Id}",
                mapper.Map<ClientModel>(newClient));
          }
      }
      catch (Exception ex)
      {
          logger.LogError(
            "Failed while creating client: {ex}",
            ex);
      }
      return Results.BadRequest(
        "Failed to create client");
  });

此代码有效。我有一个简单的 Profile 用于 AutoMapper

public class ClientMappingProfile : Profile
{
    public ClientMappingProfile()
    {
        CreateMap<Client, ClientModel>()
          .ForMember(c => c.Address1, o => o.MapFrom(m => m.Address.Address1))
          .ForMember(c => c.Address2, o => o.MapFrom(m => m.Address.Address2))
          .ForMember(c => c.Address3, o => o.MapFrom(m => m.Address.Address3))
          .ForMember(c => c.CityTown, o => o.MapFrom(m => m.Address.CityTown))
          .ForMember(c => c.PostalCode, o => o.MapFrom(m => m.Address.PostalCode))
          .ForMember(c => c.Country, o => o.MapFrom(m => m.Address.Country))
          .ReverseMap();
    }
}

我写了一个 NUnit 测试和一个 xUnit 测试。在这两种情况下,当我调用 API 时,我收到错误

Program: Error: Failed while creating client: AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.

Mapping types: ClientModel -> Client MinimalApis.Models.ClientModel -> MinimalApis.Data.Entities.Client at lambda_method92(Closure , Object , Client , ResolutionContext )

如何在主项目中使用配置文件?完整的源代码在 GitHub.

您可以使用以下代码实例化具有特定配置文件的 AutoMapper class:

var configuration = new MapperConfiguration(cfg => cfg.AddProfile(ClientMappingProfile));
var mapper = new Mapper(configuration);

要使用依赖注入来做到这一点:

services.AddAutoMapper(config =>
{
    config.AddProfile(ClientMappingProfile);
});

当您 运行 您的项目正常时,Assembly.GetEntryAssembly() 将解析为您项目的程序集(包含 Profile classes 的程序集)。当您通过单元测试项目启动项目时,入口点实际上是该单元测试。

这意味着此代码实际上并未找到配置文件,因为它们不在该程序集中:

builder.Services
  .AddAutoMapper(Assembly.GetEntryAssembly());

通常我在这种情况下所做的是使用typeof(someAssemblyInMyProject).Assembly。在这个例子中,我使用 Program 但任何 class 都应该可以工作,只要它与 Profile classes:

在同一个项目中
builder.Services
  .AddAutoMapper(typeof(Program).Assembly);

现在,无论入口程序集是什么,您仍然可以找到正确的配置文件列表。