自动映射:从静态API迁移

29

https://github.com/AutoMapper/AutoMapper/wiki/Migrating-from-static-API

这个变更破坏了我的系统。

更新之前,我使用:

===> Startup.cs

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
    ...
        MyAutoMapperConfiguration.Configure();
    }
}

===> MyAutoMapperConfiguration.cs

===> 我的自动映射配置文件 MyAutoMapperConfiguration.cs

public class MyAutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(a =>
        {
            a.AddProfile<AbcMappingProfile>();
            a.AddProfile<XyzMappingProfile>();
            a.AddProfile<QweMappingProfile>();
        });
    }
}

===> AbcMappingProfile.cs

=>

===> AbcMappingProfile.cs

public class AbcMappingProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<AbcEditViewModel, Abc>();
        Mapper.CreateMap<Abc, AbcEditViewModel>();
        ...
    }
}

错误:

'Mapper.CreateMap()'已过时:'静态API将在5.0版本中删除。请使用 MapperConfiguration 实例并根据需要进行静态存储。使用 CreateMapper 创建映射器实例。'

现在我可以使用 Mapper.Map。那么我该如何使用它呢?

4个回答

48

改为:

Mapper.CreateMap<AbcEditViewModel, Abc>();

新的语法是:

var config = new MapperConfiguration(cfg => {
  cfg.CreateMap<AbcEditViewModel, Abc>();
});

那么:

IMapper mapper = config.CreateMapper();
var source = new AbcEditViewModel();
var dest = mapper.Map<AbcEditViewModel, Abct>(source);

(更多示例源代码)


我找不到链接中的 Mapper.Initialize(),请提供更多信息。 - Asp.net
5
是的,在Startup.cs中配置AutoMapper并在控制器中使用它确实存在很多困惑。现在是否必须注入它呢? - Blake Rivell
2
它不一定非得这样,但考虑到 DI 的精神(.NET Core 正试图推广),或许应该这样做。 - NikolaiDante
2
很高兴我有时会从第三方抽象类中继承。这意味着我只需要更新少量的引用。 - Jose A

10

使用IMapperConfigurationExpression扩展而不是Automapper Profile:

映射配置:

public static class AutoMapperConfig
{
    public static IMapperConfigurationExpression AddAdminMapping(
        this IMapperConfigurationExpression configurationExpression)
    {
        configurationExpression.CreateMap<Job, JobRow>()
            .ForMember(x => x.StartedOnDateTime, o => o.PreCondition(p => p.StartedOnDateTimeUtc.HasValue))
            .ForMember(x => x.StartedOnDateTime, o => o.MapFrom(p => p.StartedOnDateTimeUtc.Value.DateTime.ToLocalTime()))
            .ForMember(x => x.FinishedOnDateTime, o => o.PreCondition(p => p.FinishedOnDateTimeUtc.HasValue))
            .ForMember(x => x.FinishedOnDateTime, o => o.MapFrom(p => p.FinishedOnDateTimeUtc.Value.DateTime.ToLocalTime()));

        return configurationExpression;
    }
}

整合 (Startup.cs 等):

        var mappingConfig = new AutoMapper.MapperConfiguration(cfg =>
        {
            cfg.AddAdminMapping();
        });

        services.AddSingleton(x => mappingConfig.CreateMapper());

在这种情况下,“services”是什么,我如何将映射器引入我的MVC控制器中? - Petrus Theron
@PetrusTheron服务是IServiceCollection,它由Asp.net Core FW注入到您的public void ConfigureServices(IServiceCollection services)方法中。 - Skorunka František

8

依赖注入给我的旧项目增加了很多我不想处理的复杂性。由于同一库被许多不同技术(Webforms、MVC、Azure Service等)调用,所以依赖注入会强制我重写几个方法或传递IMapper。

所以我只是在8.0中对它做了反向工程,并为其编写了一个包装器。

public static class MapperWrapper 
{
    private const string InvalidOperationMessage = "Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.";
    private const string AlreadyInitialized = "Mapper already initialized. You must call Initialize once per application domain/process.";

    private static IConfigurationProvider _configuration;
    private static IMapper _instance;

    private static IConfigurationProvider Configuration
    {
        get => _configuration ?? throw new InvalidOperationException(InvalidOperationMessage);
        set => _configuration = (_configuration == null) ? value : throw new InvalidOperationException(AlreadyInitialized);
    }

    public static IMapper Mapper
    {
        get => _instance ?? throw new InvalidOperationException(InvalidOperationMessage);
        private set => _instance = value;
    }

    public static void Initialize(Action<IMapperConfigurationExpression> config)
    {
        Initialize(new MapperConfiguration(config));
    }

    public static void Initialize(MapperConfiguration config)
    {
        Configuration = config;
        Mapper = Configuration.CreateMapper();
    }

    public static void AssertConfigurationIsValid() => Configuration.AssertConfigurationIsValid();
}

像之前的版本一样初始化它

public static class AutoMapperConfig
{
    public static void Configure()
    {
        MapperWrapper.Initialize(cfg =>
        {
            cfg.CreateMap<Foo1, Foo2>();              
        });

        MapperWrapper.AssertConfigurationIsValid();
    }
}

只需在启动时调用它(Global.asax等)即可。

AutoMapperConfig.Configure();

然后你所要做的就是在所有静态调用之前加上MapperWrapper。这样一切就像以前一样工作。

 MapperWrapper.Mapper.Map<Foo2>(Foo1);

0
Ben Walters:依賴注入為我的舊項目增加了一整個層次的複雜性,我只是不想處理它...
此外,您可以使用 using 語句應用類別別名,而無需更改代碼,只需更改 using 語句。
定義類別的 using 指示詞和 using 別名: https://learn.microsoft.com/zh-tw/dotnet/csharp/language-reference/keywords/using-directive#example-2

--

你的类实现以确保兼容性。

namespace AutoMappers
{
  public class Mapper
  {
    public static void Initialize(Action<AutoMapper.IMapperConfigurationExpression> config)
    {
      ...
    }
  }
}

将“using AutoMapper”更改为“using Mapper = AutoMappers.Mapper”。

using Mapper = AutoMappers.Mapper; <-- using statement changed

namespace ...
{
  public class ...
  {
    public ...(...)
    {
      Mapper.Initialize(cfg => cfg.CreateMap<TSource1, TDestination1>()); <-- other code line kept originally

--


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接