Global.asax中的多个AutoMapper.Configure()

时间:2013-12-06 18:21:14

标签: c# asp.net asp.net-web-api automapper

我正在使用AutoMapper在DTO对象和业务对象之间进行映射。我有两个AutoMapperConfiguration.cs文件 - 一个在我的服务层,另一个在我的web api层。

如以下链接中的答案所示 Where to place AutoMapper.CreateMaps?

我在Global.asax类

中调用这两个文件的Configure()
AutoMapperWebConfiguration.Configure();
AutoMapperServiceConfiguration.Configure();

但似乎我的Service Configure调用(第二次调用)正在覆盖web api层的映射(第一次调用),并且我得到一个异常,说明Mapping缺失了。

如果我将配置调用反向看起来像这样

AutoMapperServiceConfiguration.Configure();
AutoMapperWebConfiguration.Configure();

我没有得到web api映射的异常,但我得到了Service层的相同映射异常。

我做错了什么,因为这在上面的堆栈溢出链接中明确标记为答案?

这是我的代码:

public static class AutoMapperServiceConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<CmciFlowTestToGenericFlowTestSimpleMappingProfile>();
            x.AddProfile<FsrsFlowTestToGenericFlowTestSimpleMappingProfile>();
        });
    }
}

public class FsrsFlowTestToGenericFlowTestSimpleMappingProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<FsrsFlowTest, GenericFlowTest>()
            .ConvertUsing<FsrsFlowTestToGenericFlowTestSimpleConverter>();
    }
}

public class FsrsFlowTestToGenericFlowTestSimpleConverter : TypeConverter<FsrsFlowTest, GenericFlowTest>
{
    protected override GenericFlowTest ConvertCore(FsrsFlowTest source)
    {
        if (source == null)
        {
            return null;
        }

        return new GenericFlowTest
            {
                FlowTestDate = source.FlowTestDates,
                StaticPsi = source.HydrantStaticPsi.ToString(),
                ResidualPsi = source.HydrantResidualPsi.ToString(),
                TotalFlow = source.NffGallonsPerMinute.ToString(),
                FlowTestLocation = source.FsrsFlowTestLocations.Any()
                          ? source.FsrsFlowTestLocations.First().LocationDescription
                          : null
            };
    }

public class CmciFlowTestToGenericFlowTestSimpleMappingProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<CmciFlowTest, GenericFlowTest>()
            .ConvertUsing<CmciFlowTestToGenericFlowTestSimpleConverter>();
    }
}

public class CmciFlowTestToGenericFlowTestSimpleConverter : TypeConverter<CmciFlowTest, GenericFlowTest>
{
    protected override GenericFlowTest ConvertCore(CmciFlowTest source)
    {
        if (source == null)
        {
            return null;
        }

        return new GenericFlowTest
            {
                FlowTestDate = source.FlowTestDates,
                StaticPsi = source.HydrantStaticPsi.ToString(),
                ResidualPsi = source.HydrantResidualPsi.ToString(),
                TotalFlow = source.CalculatedHydrantGallonsPerMinute.ToString(),
                FlowTestLocation = source.StaticLocationHydrantFlowPSI
            };
    }
}    

public static class AutoMapperWebConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
            {
                x.AddProfile<ServiceToWebApiMappingProfile>();
                x.AddProfile<WebApiToServiceMappingProfile>();
            });
    }
}

public class ServiceToWebApiMappingProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<ServiceFlowTest, FlowTest>();
    }
}

public class WebApiToServiceMappingProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<PropertyAddress, ServicePropertyAddress>();
    }
}

要解决此问题,我将在AutoMapperWebConfiguration类中添加服务配置文件,并仅在global.asax中调用AutoMapperWebConfiguration.Configure()。

3 个答案:

答案 0 :(得分:29)

Mapper.Initialize 的调用会重置映射器,以便擦除以前所有内容。

将对AddProfile的调用移到一个Mapper.Initialize调用中,所有调用都应该很好:

Mapper.Initialize(x =>
{
    x.AddProfile<CmciFlowTestToGenericFlowTestSimpleMappingProfile>();
    x.AddProfile<FsrsFlowTestToGenericFlowTestSimpleMappingProfile>();
    x.AddProfile<ServiceToWebApiMappingProfile>();
    x.AddProfile<WebApiToServiceMappingProfile>();
});

答案 1 :(得分:11)

@GruffBunny的回答是正确的,但我试图让它更适合可伸缩性(例如,如果你有很多复杂的tmp = (Tuple<bool,T,object>) (object) I(o.ToString()); 方法,并且将来可能会增加更多)。

我是通过在Mapper.Initialize()个文件的所有中实现以下结构来实现此目的的:

将现有AutoMapperConfiguration.cs方法中的Action<IConfiguration>解压缩为公共属性

我在每个人中都称它为Mapper.Initialize()

ConfigAction

这使您可以从需要调用public static Action<IConfiguration> ConfigAction = new Action<IConfiguration>(x => { x.AddProfile<SomeProfile>(); x.AddProfile<SomeOtherProfileProfile>(); //... more profiles }); 的任何地方调用操作。

现在Mapper.Initialize内的

Mapper.Initialize()仅引用此属性

Configure()

然后,您可以在对public static void Configure() { Mapper.Initialize(ConfigAction); } 的单一集中电话中调用所有不同的ConfigAction

Mapper.Initialize()中的

AutoMapperConfiguration.Configure()变为

Application_Start()

这样就无需将方法正文从每个单独的Mapper.Initialize(x => { Project1.AutoMapperConfiguration.ConfigAction.Invoke(x); Project2.AutoMapperConfiguration.ConfigAction.Invoke(x); Project3.AutoMapperConfiguration.ConfigAction.Invoke(x); //... keep adding as your project grows }); 调用复制并粘贴到您的中央呼叫中。 DRY 等等。

答案 2 :(得分:6)

更新@theetiman对AutoMapper 5.2&amp; amp; .NET Core。

public static class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(ConfigAction);
    }

    public static Action<IMapperConfigurationExpression> ConfigAction = cfg =>
    {
        cfg.AddProfile<SomeProfile>();            
        cfg.AddProfile<SomeOtherProfileProfile>();
    };
}

API或网络启动。

public Startup(IHostingEnvironment env)
{
    Mapper.Initialize(x =>
    {
        Project1.AutoMapperConfiguration.ConfigAction.Invoke(x);
        Project2.AutoMapperConfiguration.ConfigAction.Invoke(x);
        Project3.AutoMapperConfiguration.ConfigAction.Invoke(x);
    });
}