AutoMapper.CreateMapsをどこに配置しますか?


216

アプリケーションで使用AutoMapperしていASP.NET MVCます。AutoMapper.CreateMapオーバーヘッドが多いので、別の場所に移動するように言われました。これらの呼び出しを1か所にまとめるようにアプリケーションを設計する方法がよくわかりません。

Webレイヤー、サービスレイヤー、データレイヤーがあります。それぞれ独自のプロジェクト。私はNinjectすべてをDI するために使用します。AutoMapperWebレイヤーとサービスレイヤーの両方で利用します。

では、AutoMapperCreateMapの設定はどうなっていますか?どこに置くの?どのように呼びますか?

回答:


219

静的クラスであれば問題ありません。それはすべて大会についてです。

私たちの慣習では、各「レイヤー」(Web、サービス、データ)にはと呼ばれる単一のファイルがありAutoMapperXConfiguration.cs、と呼ばれる単一のメソッドConfigure()Xあり、はレイヤーです。

Configure()方法は次に呼び出しprivate各エリアのための方法を。

これがWeb層構成の例です。

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      ConfigureUserMapping();
      ConfigurePostMapping();
   }

   private static void ConfigureUserMapping()
   {
      Mapper.CreateMap<User,UserViewModel>();
   } 

   // ... etc
}

「集約」(ユーザー、投稿)ごとにメソッドを作成するため、物事は適切に分離されます。

次にあなたのGlobal.asax

AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc

それは一種の「言葉のインターフェース」のようなものです-強制することはできませんが、それを期待しているので、必要に応じてコーディング(およびリファクタリング)できます。

編集:

AutoMapper プロファイルを使用するようにしたので、上の例は次のようになります。

public static class AutoMapperWebConfiguration
{
   public static void Configure()
   {
      Mapper.Initialize(cfg =>
      {
        cfg.AddProfile(new UserProfile());
        cfg.AddProfile(new PostProfile());
      });
   }
}

public class UserProfile : Profile
{
    protected override void Configure()
    {
         Mapper.CreateMap<User,UserViewModel>();
    }
}

はるかにクリーン/より堅牢。


2
@AliRızaAdıyahşi両方のプロジェクトにマッピングファイルが必要です。コアにはAutoMapperCoreConfigurationがあり、UIにはAutoMapperWebConfigurationが必要です。Web構成では、コア構成からプロファイルを追加する必要があります。
RPM1984

7
Mapper.Initialize各構成クラスを呼び出すと、追加された以前のプロファイルが上書きされますか?もしそうなら、初期化の代わりに何を使用すべきですか?
Cody

4
これはあなたのウェブAPIプロジェクトにあなたのサービスとドメイン層への参照を持たせませんか?
Chazt3n 2015年

3
Web-> Service-> BLL-> DALの場合。私のエンティティは私のDALにあります。WebまたはサービスからDALへの参照を与えたくありません。どうすれば初期化できますか?
Vyache

19
AutoMapper 4.2の時点で廃止されMapper.CreateMap()ました。 'Mapper.Map<TSource, TDestination>(TSource, TDestination)' is obsolete: 'The static API will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed. Use CreateMapper to create a mapper instance.'。新しい要件に準拠するように例をどのように更新しますか?
ᴍᴀᴛᴛʙᴀᴋᴇʀ

34

Webプロジェクトがそのアセンブリを参照している限り、実際にどこにでも配置できます。この状況では、Webレイヤーとサービスレイヤーからアクセスできるように、サービスレイヤーに配置します。コンソールアプリを実行するか、ユニットテストプロジェクトを実行している場合、マッピング構成はそれらのプロジェクトからも利用できます。

次にGlobal.asaxで、すべてのマップを設定するメソッドを呼び出します。下記参照:

AutoMapperBootStrapper.csファイル

public static class AutoMapperBootStrapper
{
     public static void BootStrap()
     {  
         AutoMapper.CreateMap<Object1, Object2>();
         // So on...


     }
}

アプリケーション起動時のGlobal.asax

電話するだけ

AutoMapperBootStrapper.BootStrap();

今、この方法に反対する人もいるでしょうが、有効な議論のあるいくつかのSOLID原則に違反しています。ここは読書用です。

BootstrapperでAutomapperを構成すると、Open-Closed Principleに違反しますか?


13
この。適切な「ハードコア」アーキテクチャーに向けたすべてのステップには、指数関数的に多くのコードが含まれるようです。これは簡単; そこにいるコーダーの99.9%はそれで十分です。そして、あなたの同僚はシンプルさを高く評価するでしょう。はい、誰でもオープン-クローズの原則に関する問題を読む必要がありますが、トレードオフについても考える必要があります。
アノン

AutoMapperBootStrapperクラスをどこで作成しましたか?
user6395764 2018

16

更新:SelfProfiler AutoMapper v2から削除されたため、ここに投稿されたアプローチはもはや有効ではありません。

私はトアイと同様のアプローチを取るでしょう。しかし、組み込みSelfProfiler<>クラスを使用してマップを処理し、Mapper.SelfConfigure関数を使用して初期化します。

このオブジェクトをソースとして使用:

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string GetFullName()
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}

そしてこれらは目的地として:

public class UserViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserWithAgeViewModel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Age { get; set; }
}

次のプロファイルを作成できます。

public class UserViewModelProfile : SelfProfiler<User,UserViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserViewModel> map)
    {
    //This maps by convention, so no configuration needed
    }
}

public class UserWithAgeViewModelProfile : SelfProfiler<User, UserWithAgeViewModel>
{
    protected override void DescribeConfiguration(IMappingExpression<User, UserWithAgeViewModel> map)
    {
    //This map needs a little configuration
        map.ForMember(d => d.Age, o => o.MapFrom(s => DateTime.Now.Year - s.BirthDate.Year));
    }
}

アプリケーションで初期化するには、このクラスを作成します

 public class AutoMapperConfiguration
 {
      public static void Initialize()
      {
          Mapper.Initialize(x=>
          {
              x.SelfConfigure(typeof (UserViewModel).Assembly);
              // add assemblies as necessary
          });
      }
 }

次の行をglobal.asax.csファイルに追加します。 AutoMapperConfiguration.Initialize()

これで、マッピングクラスを意味のある場所に配置でき、1つのモノリシックマッピングクラスについて心配する必要がなくなります。


3
参考までに、SelfProfilerクラスはAutomapper v2から廃止されました。
マットハニーカット2014年

15

以下を遵守する方のために:

  1. iocコンテナーの使用
  2. このためにオープンクローズを壊したくない
  3. モノリシック構成ファイルが好きではありません

私はプロファイル間でコンボを行い、iocコンテナーを活用しました:

IoC構成:

public class Automapper : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Profile>().WithServiceBase());

        container.Register(Component.For<IMappingEngine>().UsingFactoryMethod(k =>
        {
            Profile[] profiles = k.ResolveAll<Profile>();

            Mapper.Initialize(cfg =>
            {
                foreach (var profile in profiles)
                {
                    cfg.AddProfile(profile);
                }
            });

            profiles.ForEach(k.ReleaseComponent);

            return Mapper.Engine;
        }));
    }
}

構成例:

public class TagStatusViewModelMappings : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
    }
}

使用例:

public class TagStatusController : ApiController
{
    private readonly IFooService _service;
    private readonly IMappingEngine _mapper;

    public TagStatusController(IFooService service, IMappingEngine mapper)
    {
        _service = service;
        _mapper = mapper;
    }

    [Route("")]
    public HttpResponseMessage Get()
    {
        var response = _service.GetTagStatus();

        return Request.CreateResponse(HttpStatusCode.Accepted, _mapper.Map<List<ViewModels.TagStatusViewModel>>(response)); 
    }
}

トレードオフは、静的マッパーの代わりにIMappingEngineインターフェースでマッパーを参照する必要があることですが、それは私が共存できる規約です。


14

上記のすべてのソリューションは、(app_startまたは任意の場所から)呼び出す静的メソッドを提供し、マッピング構成の一部を構成するために他のメソッドを呼び出す必要があります。ただし、モジュール式のアプリケーションを使用している場合、そのモジュールはいつでもアプリケーションにプラグインおよびプラグアウトする可能性があり、これらのソリューションは機能しません。私が使用することをお勧めWebActivator上で実行するためにいくつかのメソッドを登録することができ、ライブラリをapp_pre_startし、app_post_start任意の場所:

// in MyModule1.dll
public class InitMapInModule1 {
    static void Init() {
        Mapper.CreateMap<User, UserViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule1), "Init")]

// in MyModule2.dll
public class InitMapInModule2 {
    static void Init() {
        Mapper.CreateMap<Blog, BlogViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// in MyModule3.dll
public class InitMapInModule3 {
    static void Init() {
        Mapper.CreateMap<Comment, CommentViewModel>();
        // other stuffs
    }
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]

// and in other libraries...

WebActivatorNuGet経由でインストールできます。


2
私は最近同じ結論に達しました。これにより、マップ作成コードは、それを使用するコードに近づけます。この方法により、MVCコントローラーがはるかに保守しやすくなります。
mfras3r 2012

どこから始めればよいですか、例を提供できますか?ブログのリンクが機能しない...
Vyache

1
@Vyacheそれはかなり明確です!中MyModule1のプロジェクト(または任意のプロジェクトの名前です)だけという名前のクラスを作成InitMapInModule1し、ファイル内のコードを置きます。他のモジュールについても同じようにします。
ravy amiry

ゴッチャ、私は実際にそれを試してみました。NugetからWebActivatorをクラスライブラリ(DAL)に追加し、静的なAutoMapperDalConfigurationクラスを作成して、マップを構成および初期化するための@ RPM1984実装を作成しました。私はプロファイルスルーを使用していません。ありがとうございました。
Vyache

10

ベストアンサーに加えて、Autofac IoCライブラリーを使用していくつかの自動化を追加するのが良い方法です。これにより、開始に関係なくプロファイルを定義するだけです。

   public static class MapperConfig
    {
        internal static void Configure()
        {

            var myAssembly = Assembly.GetExecutingAssembly();

            var builder = new ContainerBuilder();

            builder.RegisterAssemblyTypes(myAssembly)
                .Where(t => t.IsSubclassOf(typeof(Profile))).As<Profile>();

            var container = builder.Build();

            using (var scope = container.BeginLifetimeScope())
            {
                var profiles = container.Resolve<IEnumerable<Profile>>();

                foreach (var profile in profiles)
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfile(profile);
                    });                    
                }

            }

        }
    }

そして、この行をApplication_Startメソッドで呼び出します:

MapperConfig.Configure();

上記のコードは、すべてのProfileサブクラスを見つけて、それらを自動的に開始します。


7

すべてのマッピングロジックを1つの場所に配置することは、私にとって良い習慣ではありません。なぜなら、マッピングクラスは非常に大きく、維持するのが非常に難しいからです。

マッピング関連のものは、ViewModelクラスと同じcsファイルに配置することをお勧めします。この規則に従って、必要なマッピング定義に簡単に移動できます。さらに、マッピングクラスを作成するときに、同じファイル内にあるため、ViewModelプロパティをより速く参照できます。

したがって、ビューモデルクラスは次のようになります。

public class UserViewModel
{
    public ObjectId Id { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public string Email { get; set; }

    public string Password { get; set; }
}

public class UserViewModelMapping : IBootStrapper // Whatever
{
    public void Start()
    {
        Mapper.CreateMap<User, UserViewModel>();
    }
}

9
どのように呼びますか?
Shawn Mclean

1
:私は、ファイルルールごとに1クラスたどるstackoverflow.com/q/2434990/1158845
Umair

同様の精神は、VelirのブログMVCでのAutoMapperのマップ構成の整理
xmedeko

5

AutoMapperの新しいバージョンから、静的メソッドMapper.Map()を使用することは非推奨になりました。したがって、MapperConfigurationを静的プロパティとしてMvcApplication(Global.asax.cs)に追加し、それを使用してMapperのインスタンスを作成できます。

App_Start

public class MapperConfig
{
    public static MapperConfiguration MapperConfiguration()
    {
        return new MapperConfiguration(_ =>
        {
            _.AddProfile(new FileProfile());
            _.AddProfile(new ChartProfile());
        });
    }
}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    internal static MapperConfiguration MapperConfiguration { get; private set; }

    protected void Application_Start()
    {
        MapperConfiguration = MapperConfig.MapperConfiguration();
        ...
    }
}

BaseController.cs

    public class BaseController : Controller
    {
        //
        // GET: /Base/
        private IMapper _mapper = null;
        protected IMapper Mapper
        {
            get
            {
                if (_mapper == null) _mapper = MvcApplication.MapperConfiguration.CreateMapper();
                return _mapper;
            }
        }
    }

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


3

を使用している(失われた)人のために:

  • WebAPI 2
  • SimpleInjector 3.1
  • AutoMapper 4.2.1(プロファイルあり)

Automapperを「新しい方法」で統合する方法を以下に示します。また、巨大な本のおかげで答え(質問)

1-「ProfileMappers」というWebAPIプロジェクトにフォルダーを作成しました。このフォルダに、マッピングを作成するすべてのプロファイルクラスを配置します。

public class EntityToViewModelProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<User, UserViewModel>();
    }

    public override string ProfileName
    {
        get
        {
            return this.GetType().Name;
        }
    }
}

2-私のApp_Startには、SimpleInjectorコンテナーを構成するSimpleInjectorApiInitializerがあります。

public static Container Initialize(HttpConfiguration httpConfig)
{
    var container = new Container();

    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();

    //Register Installers
    Register(container);

    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    //Verify container
    container.Verify();

    //Set SimpleInjector as the Dependency Resolver for the API
    GlobalConfiguration.Configuration.DependencyResolver =
       new SimpleInjectorWebApiDependencyResolver(container);

    httpConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

    return container;
}

private static void Register(Container container)
{
     container.Register<ISingleton, Singleton>(Lifestyle.Singleton);

    //Get all my Profiles from the assembly (in my case was the webapi)
    var profiles =  from t in typeof(SimpleInjectorApiInitializer).Assembly.GetTypes()
                    where typeof(Profile).IsAssignableFrom(t)
                    select (Profile)Activator.CreateInstance(t);

    //add all profiles found to the MapperConfiguration
    var config = new MapperConfiguration(cfg =>
    {
        foreach (var profile in profiles)
        {
            cfg.AddProfile(profile);
        }
    });

    //Register IMapper instance in the container.
    container.Register<IMapper>(() => config.CreateMapper(container.GetInstance));

    //If you need the config for LinqProjections, inject also the config
    //container.RegisterSingleton<MapperConfiguration>(config);
}

3-Startup.cs

//Just call the Initialize method on the SimpleInjector class above
var container = SimpleInjectorApiInitializer.Initialize(configuration);

4-次に、コントローラーで通常どおりIMapperインターフェイスを挿入します。

private readonly IMapper mapper;

public AccountController( IMapper mapper)
{
    this.mapper = mapper;
}

//Using..
var userEntity = mapper.Map<UserViewModel, User>(entity);

いくつかの詳細を少し調整すると、このアプローチはMVCでもうまく機能します。
Nick Coad

githubにデモの例を追加してください
Mohammad Daliri

3

AutoMapperの新しいバージョン(5.x)を使用するvb.netプログラマー向け。

Global.asax.vb:

Public Class MvcApplication
    Inherits System.Web.HttpApplication

    Protected Sub Application_Start()
        AutoMapperConfiguration.Configure()
    End Sub
End Class

AutoMapperConfiguration:

Imports AutoMapper

Module AutoMapperConfiguration
    Public MapperConfiguration As IMapper
    Public Sub Configure()
        Dim config = New MapperConfiguration(
            Sub(cfg)
                cfg.AddProfile(New UserProfile())
                cfg.AddProfile(New PostProfile())
            End Sub)
        MapperConfiguration = config.CreateMapper()
    End Sub
End Module

プロファイル:

Public Class UserProfile
    Inherits AutoMapper.Profile
    Protected Overrides Sub Configure()
        Me.CreateMap(Of User, UserViewModel)()
    End Sub
End Class

マッピング:

Dim ViewUser = MapperConfiguration.Map(Of UserViewModel)(User)

私はあなたの答えを試しましたが、この行にエラーが表示されています:Dim config = New MapperConfiguration(//これらの引数でアクセス可能な「New」を呼び出すことができないため、オーバーロードの解決に失敗しました:あなたはそれで私を助けてくれますか?
barsan '12 / 12/23

@barsan:すべてのプロファイルクラスを正しく構成しましたか(UserProfileおよびPostProfile)?私にとってはAutomapperバージョン5.2.0で動作します。
roland 16

新しいバージョン6.0がリリースされました。したがって、Protected Overrides Sub Configure()は非推奨です。すべての滞在同じですが、この行は次のようになりますPublic Sub New()
ROLAND
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.