AutoMapper 10.0使用教程


什么是AutoMapper

我們都知道,引用類型直接賦值傳遞的是地址,如果直接賦值,則改變一個類的屬性的同時也在改變另一個類。

所以,當我們需要實現像int類型直接賦值更改互不影響的效果時,我們需要映射

將A類映射賦值到B類的時候,我們就需要一個對象映射器(object-object),也就是AutoMapper。

我們只需要提前配置好要映射的兩個類,即可輕松實現反射。

配置

使用MapperConfiguration配置

創建一個 MapperConfiguration 實例並通過構造函數初始化配置:

var config = new MapperConfiguration(cfg => {
    cfg.CreateMap<Foo, Bar>();
    cfg.AddProfile<FooProfile>();
});

MapperConfiguration 實例可以靜態存儲,也可以存儲在靜態字段或依賴注入容器中。一旦創建,它就不能被更改/修改。

var configuration = new MapperConfiguration(cfg => {
    cfg.CreateMap<Foo, Bar>();
    cfg.AddProfile<FooProfile>();
});

注:從9.0開始,靜態 API 不再可用。

使用Profile Instances配置

組織映射配置的一個好方法是使用配置文件。創建從 Profile 繼承的類,並將配置放入構造函數中:

// This is the approach starting with version 5
public class OrganizationProfile : Profile
{
	public OrganizationProfile()
	{
		CreateMap<Foo, FooDto>();
		// Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
	}
}

// How it was done in 4.x - as of 5.0 this is obsolete:
// public class OrganizationProfile : Profile
// {
// protected override void Configure()
// {
// CreateMap<Foo, FooDto>();
// }
// }

在早期版本中,使用 Configure 方法而不是構造函數。從版本5開始,Configure ()就過時了。它將在6.0版本中被刪除。

配置文件中的配置只應用於配置文件中的映射。應用於根配置的配置應用於創建的所有映射。

Assembly Scanning for auto configuration (自動配置程序集掃描)

配置文件可以通過多種方式直接添加到主映射器配置中:

cfg.AddProfile<OrganizationProfile>();
cfg.AddProfile(new OrganizationProfile());
or by automatically scanning for profiles:

或者通過自動掃描檔案:

// Scan for all profiles in an assembly
// ... using instance approach:
var config = new MapperConfiguration(cfg => {
    cfg.AddMaps(myAssembly);
});
var configuration = new MapperConfiguration(cfg => cfg.AddMaps(myAssembly));

// Can also use assembly names:
var configuration = new MapperConfiguration(cfg =>
    cfg.AddMaps(new [] {
        "Foo.UI",
        "Foo.Core"
    });
);

// Or marker types for assemblies:
var configuration = new MapperConfiguration(cfg =>
    cfg.AddMaps(new [] {
        typeof(HomeController),
        typeof(Entity)
    });
);

AutoMapper 將掃描指定的程序集,從 Profile 繼承類,並將它們添加到配置中。

Naming Conventions(命名約定)

您可以設置源和目標命名約定

var configuration = new MapperConfiguration(cfg => {
  cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
  cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
});

這將把以下屬性映射到彼此: property _ name-> PropertyName

您還可以將其設置為每個配置文件級別

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
    DestinationMemberNamingConvention = new PascalCaseNamingConvention();
    //Put your CreateMap... Etc.. here
  }
}

如果你不需要變數命名原則,你可以使用精確匹配命名協議。

Replacing characters (替換字符)

還可以在成員名稱匹配過程中替換源成員中的單個字符或整個單詞:

public class Source
{
    public int Value { get; set; }
    public int Ävíator { get; set; }
    public int SubAirlinaFlight { get; set; }
}
public class Destination
{
    public int Value { get; set; }
    public int Aviator { get; set; }
    public int SubAirlineFlight { get; set; }
}

We want to replace the individual characters, and perhaps translate a word:

我們想要替換單個字符,或許可以翻譯一個單詞:

var configuration = new MapperConfiguration(c =>
{
    c.ReplaceMemberName("Ä", "A");
    c.ReplaceMemberName("í", "i");
    c.ReplaceMemberName("Airlina", "Airline");
});

Recognizing pre/postfixes 識別前/后綴

有時候,源/目標屬性會有共同的前/后綴,這導致您必須執行一系列自定義成員映射,因為名稱不匹配。為了解決這個問題,您可以識別前/后綴:

public class Source {
    public int frmValue { get; set; }
    public int frmValue2 { get; set; }
}
public class Dest {
    public int Value { get; set; }
    public int Value2 { get; set; }
}
var configuration = new MapperConfiguration(cfg => {
    cfg.RecognizePrefixes("frm");
    cfg.CreateMap<Source, Dest>();
});

默認情況下,AutoMapper 識別前綴“ Get” ,如果您需要清除前綴:

var configuration = new MapperConfiguration(cfg => {
    cfg.ClearPrefixes();
    cfg.RecognizePrefixes("tmp");
});

Global property/field filtering 全局屬性/字段篩選

默認情況下,AutoMapper 會嘗試映射每個公共屬性/字段。您可以使用屬性/字段過濾器過濾出屬性/字段:

var configuration = new MapperConfiguration(cfg =>
{
	// don't map any fields
	cfg.ShouldMapField = fi => false;

	// map properties with a public or private getter
	cfg.ShouldMapProperty = pi =>
		pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate);
});
Configuring

Visibility 配置可見性

默認情況下,AutoMapper 只能識別公共成員。它可以映射到私有 setters,但是如果整個屬性都是 private/internal,則會跳過 internal/private 方法和屬性。要指示 AutoMapper 識別具有其他可視性的成員,請覆蓋默認過濾器 ShouldMapField 和/或 shouldmapproty:

var configuration = new MapperConfiguration(cfg =>
{
    // map properties with public or internal getters
    cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
    cfg.CreateMap<Source, Destination>();
});

Configuration compilation 配置編譯

由於表達式編譯可能占用位資源,因此 AutoMapper 在第一個映射上編譯類型映射計划。然而,這種行為並不總是可取的,所以你可以告訴 AutoMapper 直接編譯它的映射:

var configuration = new MapperConfiguration(cfg => {});
configuration.CompileMappings();

對於幾百個映射,這可能需要幾秒鍾。

Dependency Injection (依賴注入)

AutoMapper 支持使用靜態服務位置構建自定義值解析器、自定義類型轉換器和值轉換器:

var configuration = new MapperConfiguration(cfg =>
{
    cfg.ConstructServicesUsing(ObjectFactory.GetInstance);

    cfg.CreateMap<Source, Destination>();
});

或動態服務位置,用於基於實例的容器(包括子/嵌套容器) :

var mapper = new Mapper(configuration, childContainer.GetInstance);

var dest = mapper.Map<Source, Destination>(new Source { Value = 15 });

Queryable Extensions 可查詢擴展

從8.0開始,你可以使用 IMapper。ProjectTo.對於舊版本,您需要將配置傳遞給擴展方法 IQueryable。項目組 < t > (圖像提供者)。

注意 IQueryable。ProjectTo是比IMappe更有限 的映射,因為只支持基礎 LINQ 提供程序所允許的內容。這意味着不能像對 Map 那樣對值解析器和轉換器使用 DI。

例子

ASP.NET Core

有一個 NuGet 包將與這里描述的默認注入機制一起使用,並在這個項目中使用。

您可以使用配置文件定義配置。然后你讓 AutoMapper 知道哪些程序集是通過在啟動時調用 IServiceCollection 擴展方法 AddAutoMapper 定義的概要文件:

services.AddAutoMapper(profileAssembly1, profileAssembly2 /*, ...*/);
or marker types:

或者標記類型:

services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2) /*, ...*/);

現在你可以在運行時將 AutoMapper 注入到你的服務/控制器中:

public class EmployeesController {
	private readonly IMapper _mapper;

	public EmployeesController(IMapper mapper) => _mapper = mapper;

	// use _mapper.Map or _mapper.ProjectTo
}

AutoFac

Autofac for AutoMapper

Ninject

對於那些使用 Ninject 的人來說,這里是一個用於 AutoMapper 的 Ninject 模塊的例子

public class AutoMapperModule : NinjectModule
{
    public override void Load()
    {
        Bind<IValueResolver<SourceEntity, DestModel, bool>>().To<MyResolver>();

        var mapperConfiguration = CreateConfiguration();
        Bind<MapperConfiguration>().ToConstant(mapperConfiguration).InSingletonScope();

        // This teaches Ninject how to create automapper instances say if for instance
        // MyResolver has a constructor with a parameter that needs to be injected
        Bind<IMapper>().ToMethod(ctx =>
             new Mapper(mapperConfiguration, type => ctx.Kernel.Get(type)));
    }

    private MapperConfiguration CreateConfiguration()
    {
        var config = new MapperConfiguration(cfg =>
        {
            // Add all profiles in current assembly
            cfg.AddMaps(GetType().Assembly);
        });

        return config;
    }
}

Simple Injector 簡單注射器

工作流程如下:

  1. 通過 myregistry.Register 注冊你的類型
  2. MapperProvider 允許您直接將 IMapper 實例注入到其他類中
  3. 使用 propertythatdependensoniovalueresolver 解析一個值
  4. 將 IService 注入到 propertythatdependensoniovalueresolver 中,然后就可以使用了

ValueResolver 可以訪問 IService,因為我們通過 MapperConfigurationExpression. ConstructServicesUsing 注冊容器

public class MyRegistrar
{
    public void Register(Container container)
    {
        // Injectable service
        container.RegisterSingleton<IService, SomeService>();

        // Automapper
        container.RegisterSingleton(() => GetMapper(container));
    }

    private AutoMapper.IMapper GetMapper(Container container)
    {
        var mp = container.GetInstance<MapperProvider>();
        return mp.GetMapper();
    }
}

public class MapperProvider
{
    private readonly Container _container;

    public MapperProvider(Container container)
    {
        _container = container;
    }

    public IMapper GetMapper()
    {
        var mce = new MapperConfigurationExpression();
        mce.ConstructServicesUsing(_container.GetInstance);

        mce.AddMaps(typeof(SomeProfile).Assembly);

        var mc = new MapperConfiguration(mce);
        mc.AssertConfigurationIsValid();

        IMapper m = new Mapper(mc, t => _container.GetInstance(t));

        return m;
    }
}

public class SomeProfile : Profile
{
    public SomeProfile()
    {
        var map = CreateMap<MySourceType, MyDestinationType>();
        map.ForMember(d => d.PropertyThatDependsOnIoc, opt => opt.MapFrom<PropertyThatDependsOnIocValueResolver>());
    }
}

public class PropertyThatDependsOnIocValueResolver : IValueResolver<MySourceType, object, int>
{
    private readonly IService _service;

    public PropertyThatDependsOnIocValueResolver(IService service)
    {
        _service = service;
    }

    int IValueResolver<MySourceType, object, int>.Resolve(MySourceType source, object destination, int destMember, ResolutionContext context)
    {
        return _service.MyMethod(source);
    }
}

Castle Windsor

對於那些使用Castle Windsor在這里是一個例子

public class AutoMapperInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            // Register all mapper profiles
            container.Register(
                Classes.FromAssemblyInThisApplication(GetType().Assembly)
                .BasedOn<Profile>().WithServiceBase());
                
            // Register IConfigurationProvider with all registered profiles
            container.Register(Component.For<IConfigurationProvider>().UsingFactoryMethod(kernel =>
            {
                return new MapperConfiguration(configuration =>
                {
                    kernel.ResolveAll<Profile>().ToList().ForEach(configuration.AddProfile);
                });
            }).LifestyleSingleton());
            
            // Register IMapper with registered IConfigurationProvider
            container.Register(
                Component.For<IMapper>().UsingFactoryMethod(kernel =>
                    new Mapper(kernel.Resolve<IConfigurationProvider>(), kernel.Resolve)));
        }
}

Catel.IoC

對於那些使用 Catel.IoC 的用戶,下面介紹如何注冊自動控制器。首先使用配置文件定義配置。然后你讓 AutoMapper 知道在哪些程序集中這些配置文件是通過在啟動時在 ServiceLocator 中注冊 AutoMapper 定義的:

配置創建方法:

public static MapperConfiguration CreateConfiguration()
{
    var config = new MapperConfiguration(cfg =>
    {
        // Add all profiles in current assembly
        cfg.AddMaps(GetType().Assembly);
    });

    return config;
}

現在你可以在運行時將 AutoMapper 注入到你的服務/控制器中:

public class EmployeesController {
	private readonly IMapper _mapper;

	public EmployeesController(IMapper mapper) => _mapper = mapper;

	// use _mapper.Map or _mapper.ProjectTo
}

后記

本人不是大佬,只是道路先行者,在落河后,向后來的人大喊一聲,這里有坑,不要過來啊!

縱然如此,依舊有人重復着落河,重復着吶喊······

個人博客網站 Blog

技術交流Q群: 1012481075 群內有各種流行書籍資料

文章后續會在公眾號更新,微信搜索 OneByOneDotNet 即可關注。

你的一分鼓勵,我的十分動力,點贊免費,感恩回饋。喜歡就點贊評論吧,雙擊6666~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM