web-dev-qa-db-ja.com

Global.asax内の複数のAutoMapper.Configure()

AutoMapperを使用して、DTOオブジェクトとビジネスオブジェクトをマッピングしています。 AutoMapperConfiguration.csファイルが2つあります。1つはサービスレイヤーに、もう1つはWeb APIレイヤーにあります。

次のリンクの答えに示されているように AutoMapper.CreateMapsを配置する場所?

私はGlobal.asaxクラスでこれらの両方のファイルのConfigure()を呼び出しています

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

しかし、私のサービス構成呼び出し(2番目の呼び出し)がWeb APIレイヤー(最初の呼び出し)のマッピングを上書きしているようで、マッピングが見つからないという例外が発生します。

このように構成呼び出しを逆にすると

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

Web APIマッピングの例外は発生しませんが、サービスレイヤーの同じマッピング例外が発生します。

これは上記のスタックオーバーフローリンクで回答として明確にマークされているため、何か間違っていますか?

これが私のコードです:

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()のみを呼び出します。

18
manu79

Mapper.Initializeを呼び出すと、マッパーがリセットされ、以前に行ったすべてのものがワイプされます。

AddProfileへの呼び出しを1つのMapper.Initialize呼び出しに移動すると、すべて正常に動作するはずです。

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

@GruffBunnyの答えは正しいですが、スケーラビリティのために少しすっきりさせようとしました(たとえば、多くの複雑なMapper.Initialize()メソッドがあり、将来さらに追加する可能性がある場合)。

これを行うには、_AutoMapperConfiguration.cs_ファイルのallに次の構造を実装します。

既存のMapper.Initialize()メソッドから_Action<IConfiguration>_をパブリックプロパティに抽出します

それぞれを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);
}
_

その後、Mapper.Initialize()への単一の集中呼び出しで、すべての異なるConfigActionsを呼び出すことができます

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
            });
_

これにより、個々のMapper.Initialize()呼び出しから中央の呼び出しにメソッド本体をコピーして貼り付ける必要がなくなります。 [〜#〜] dry [〜#〜]およびそれすべて。

11
theyetiman

AutoMapper 5.2と.NET Coreに対する@theyetimanの素晴らしい答えを更新する。

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

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

APIまたはWebスタートアップ。

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