如何在AutoMapper中扫描和自动配置配置文件?

28

有没有办法自动配置Automapper以扫描特定命名空间/程序集中的所有配置文件?我想做的是从给定程序集中添加映射配置文件到Automapper,并过滤特定接口,就像StructureMap中的“扫描约定”一样。

    public static void Configure()
    {
        ObjectFactory.Initialize(x =>
            {
                // Scan Assembly
                x.Scan(
                    scanner =>
                    {
                        scanner.TheCallingAssembly();
                        scanner.Convention<MyCustomConvention>();
                        scanner.WithDefaultConventions();
                    });

                // Add Registries
                x.AddRegistry(new SomeRegistry());
            });

        Debug.WriteLine(ObjectFactory.WhatDoIHave());
    }

public class MyCustomConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        if (!type.CanBeCastTo(typeof(IMyType)))
        {
            return;
        }

        string name = type.Name.Replace("SomeRubishName", String.Empty);
        registry.AddType(typeof(IMyType), type, name);            
    }

我尝试使用SelfConfigure,但找不到任何关于如何使用它来过滤配置文件的文档:

    public static void Configure()
    {
        Mapper.Initialize(x =>
                              {
                                  // My Custom profile
                                  x.AddProfile<MyMappingProfile>();

                                  // Scan Assembly
                                  x.SelfConfigure(Assembly.GetCallingAssembly());
                              });
    }

另一个问题是如何报告已经初始化的所有映射/配置文件(类似于StructureMap中的ObjectFactory.WhatDoIHave())?

10个回答

41

我也在搜索中发现了这篇帖子,但这是我实现自动映射方案的方法:

public class MyCustomMap : Profile
{
    protected override void Configure()
    {
        CreateMap<MyCustomViewModel, MyCustomObject>()
            .ForMember(dest => dest.Phone,
                        opt => opt.MapFrom(
                        src => src.PhoneAreaCode + src.PhoneFirstThree + src.PhoneLastFour));
    }
}

public static class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x => GetConfiguration(Mapper.Configuration));
    }

    private static void GetConfiguration(IConfiguration configuration)
    {
        var profiles = typeof(MyCustomMap).Assembly.GetTypes().Where(x => typeof(Profile).IsAssignableFrom(x));
        foreach (var profile in profiles)
        {
            configuration.AddProfile(Activator.CreateInstance(profile) as Profile);
        }
    }
}

因此,当我的应用程序启动时,我所调用的全部内容只有

AutoMapperConfiguration.Configure(); 

我所有的地图都已注册。


这似乎与“使用ConstructServicesUsing方法构造服务”互斥。目前正在苦苦地处理传递依赖项的问题。 - Arnis Lapsa
是的,我没有在我的映射中传递任何依赖项。你可以在一个 IoC 容器中注册它们,并让它为你解决依赖关系。我们本来会这样做的,但并没有必要。 - Jason More
2
Arnis L - 只需更改配置。将AddProfile(Activator.CreateInstance(profile) as Profile);更改为configuration.AddProfile(ServiceLocator.Current.GetInstance(profile) as Profile);(或类似的代码,具体取决于您使用的IoC容器),以启用依赖注入。非常好的答案。 - Paul Suart

23

在AutoMapper的第9个版本中,可以通过以下方式完成

var configuration = new MapperConfiguration(cfg =>
{
    // Add all Profiles from the Assembly containing this Type
    cfg.AddMaps(typeof(MyApp.SomeClass));
});
如果您正在使用 ASP.NET Core,有一个帮助程序扩展可以在 Startup.ConfigureServices 中注册所有配置文件。
// UI project
services.AddAutoMapper(Assembly.GetExecutingAssembly());
或者
// Another assembly that contains a type
services.AddAutoMapper(Assembly.GetAssembly(typeof(MyApp.SomeClass)));

18
在最新版本的AutoMapper中,可以注册多个Profile,扫描一个或多个程序集:
 Mapper.Initialize(x => x.AddProfiles(typeof(MyMappingProfile).Assembly));

使用 AutoMapper v. 6.0.2.0 进行测试。


9

是的,那将会非常棒...并且正是我正在为V2进行的大修。扫描、注册、约定等。

目前还没有一个好的“我拥有什么”功能,但我认为值得添加。


感谢AutoMapper - 没有它,我和许多其他人都会迷失方向。你知道有关如何使用Profile和相关内容的博客文章(或电子邮件线程/文章/SO帖子)吗?目前在SO上,情况有点混乱,因为有很多v1时代的示例,但几乎没有v2的内容。即使只是一个简短的句子,比如“调整你的DI以选择IXXXInitialize实现,并将其提供给XXXConfiguration的构造函数,然后让控制器需要由IConfiguration实现的IMapper绑定”,或者类似的内容也行。 - Ruben Bartelink

2

In .NET Core:

    services.AddSingleton(this.CreateMapper());
    //...
    private IMapper CreateMapper()
            => new MapperConfiguration(config => config.AddMaps(Assembly.Load("Your.Project.App")))
            .CreateMapper();

1
//AutoMapper 12.0.0
using Microsoft.Extensions.DependencyInjection;

//if the profiles are in the same assembly as the StartUp class

services.AddAutoMapper(typeof(Startup));

//else

services.AddAutoMapper(typeof(AnyProfile));

1

补充一下@Rosco的回答,您可以使用typeof(class)而不是assembly。任何类都可以工作。

services.AddAutoMapper(typeof(Startup));

那么您就不需要添加反射引用。

如果您在多个程序集中有配置文件,可以按照以下方式使用它。

services.AddAutoMapper(typeof(Startup), typeof(User));

1

我是这样做的,不知道是否是最好的方法,但在相当大的项目中它运行得非常好。

public class AutoMapperGlobalConfiguration : IGlobalConfiguration
    {
        private AutoMapper.IConfiguration _configuration;

        public AutoMapperGlobalConfiguration(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public void Configure()
        {
            //add all defined profiles
            var query = this.GetType().Assembly.GetExportedTypes()
                .Where(x => x.CanBeCastTo(typeof(AutoMapper.Profile)));

            _configuration.RecognizePostfixes("Id");

            foreach (Type type in query)
            {
                _configuration.AddProfile(ObjectFactory.GetInstance(type).As<Profile>());
            }

            //create maps for all Id2Entity converters
            MapAllEntities(_configuration);

           Mapper.AssertConfigurationIsValid();
        }

        private static void MapAllEntities(IProfileExpression configuration)
        {
            //get all types from the SR.Domain assembly and create maps that
            //convert int -> instance of the type using Id2EntityConverter
            var openType = typeof(Id2EntityConverter<>);
            var idType = typeof(int);
            var persistentEntties = typeof(SR.Domain.Policy.Entities.Bid).Assembly.GetTypes()
               .Where(t => typeof(EntityBase).IsAssignableFrom(t))
               .Select(t => new
               {
                   EntityType = t,
                   ConverterType = openType.MakeGenericType(t)
               });
            foreach (var e in persistentEntties)
            {
                var map = configuration.CreateMap(idType, e.EntityType);
                map.ConvertUsing(e.ConverterType);
            }
        }
    }
}

1
通过这种方式,我认为您无法控制配置文件依赖项的生命周期。 - Arnis Lapsa

1
类似于 @Martino 的答案,但使用 MapperConfiguration 对象。这将添加包含类型 MyProfile 的程序集中的所有配置文件。
var config = new MapperConfiguration(cfg =>
   {
      cfg.AddProfiles(typeof(MyProfile));
   });
var mapper = config.CreateMapper();

好的,但是我的资料在哪里,怎么找到它? - HerGiz

0
 public class AutoMapperAdapter : IMapper
{
    private readonly MapperConfigurationExpression _configurationExpression =
        new MapperConfigurationExpression();

    public void AssertConfigurationIsValid() { Mapper.AssertConfigurationIsValid(); }

    public void CreateMap<TSource, TDestination>()
    {
        _configurationExpression.CreateMap<TSource, TDestination>();
    }

    public void Initialize() { Mapper.Initialize(_configurationExpression); }

    public TDestination Map<TDestination>(object source)
    {
        return Mapper.Map<TDestination>(source);
    }
}

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