ASP.NET CoreでAutomapperを設定する方法


255

私は.NETに比較的慣れていないので、「古い方法」を学ぶ代わりに.NET Coreに取り組むことにしました。AutoMapper for .NET Coreの設定に関する詳細な記事はこちらにありますが、初心者向けの簡単なチュートリアルはありますか?



コアの新しいバージョン(> V1)についてSaineshwarの回答@チェックアウト stackoverflow.com/a/53455699/833878
ロビー

回答:


554

私はそれを考え出した!詳細は次のとおりです。

  1. NuGetを介して、メインのAutoMapperパッケージをソリューションに追加します。
  2. NuGetを介してAutoMapper Dependency Injection Packageをソリューションに追加します。

  3. マッピングプロファイルの新しいクラスを作成します。(メインソリューションディレクトリにクラスを作成しMappingProfile.cs、次のコードを追加しました。)例としてUserおよびUserDtoオブジェクトを使用します。

    public class MappingProfile : Profile {
        public MappingProfile() {
            // Add as many of these lines as you need to map your objects
            CreateMap<User, UserDto>();
            CreateMap<UserDto, User>();
        }
    }
  4. 次に、Startup.cs以下に示すようにAutoMapperConfigurationを追加します。

    public void ConfigureServices(IServiceCollection services) {
        // .... Ignore code before this
    
       // Auto Mapper Configurations
        var mappingConfig = new MapperConfiguration(mc =>
        {
            mc.AddProfile(new MappingProfile());
        });
    
        IMapper mapper = mappingConfig.CreateMapper();
        services.AddSingleton(mapper);
    
        services.AddMvc();
    
    }
  5. コード化されたオブジェクトをコードで呼び出すには、次のようにします。

    public class UserController : Controller {
    
        // Create a field to store the mapper object
        private readonly IMapper _mapper;
    
        // Assign the object in the constructor for dependency injection
        public UserController(IMapper mapper) {
            _mapper = mapper;
        }
    
        public async Task<IActionResult> Edit(string id) {
    
            // Instantiate source object
            // (Get it from the database or whatever your code calls for)
            var user = await _context.Users
                .SingleOrDefaultAsync(u => u.Id == id);
    
            // Instantiate the mapped data transfer object
            // using the mapper you stored in the private field.
            // The type of the source object is the first type argument
            // and the type of the destination is the second.
            // Pass the source object you just instantiated above
            // as the argument to the _mapper.Map<>() method.
            var model = _mapper.Map<UserDto>(user);
    
            // .... Do whatever you want after that!
        }
    }

これがASP.NET Coreで新しく始めるのに役立つことを願っています!.NETの世界にはまだ慣れていないので、フィードバックや批判は歓迎します。


3
リンクされた詳細な記事、lostechies.com / jimmybogard / 2016/07/20 /…はProfileクラスがどのように配置されているかを説明しています
Kieren Johnstone

22
@theutzこれらの2つのCreateMap行を、最後に.ReverseMap()でマージすることもできます。コメントするかもしれませんが、私はそれがより直感的だと思います。
アストラバガント2017年

6
ステップ3で、「AutoMapperを使用する」の追加について言及すると役立つ場合があります。上部にあるので、拡張メソッドがインポートされます。
ロックラン2017年

8
これは.net core 1.1で問題なく機能しましたが、.net core 2.0にアップグレードすると、もう機能しません。ロジックプロファイルクラスアセンブリを明示的に指定する必要があると思います。それを達成する方法をまだ研究しています。更新:ああ、答えはコメントにあります。私のプロフィールであるtypeofクラスを渡す必要があります。// services.AddAutoMapper(typeof(Startup)); // <-新しいオートマッパーバージョンはこのシグネチャを使用
Esen

3
AutoMapper v8とDependency Injection v5アドオンでは、必要なのはservices.AddAutoMapper();だけです。StartupクラスのConfigureServicesメソッドの行。私にとっては、依存クラスライブラリプロジェクトでプロファイルクラスを見つけることさえできました。
stricq 2018

68

ASP.NET CoreでAutoMapperを使用する手順。

手順1. NuGetパッケージからAutoMapper.Extensions.Microsoft.DependencyInjectionをインストールします。

ここに画像の説明を入力してください

ステップ2.「マッピング」という名前のマッピングを保持するために、ソリューションにフォルダーを作成します。

ここに画像の説明を入力してください

ステップ3. Mappingフォルダーを追加した後、「MappingProfile」という名前のクラスを追加しました。この名前は、一意でわかりやすい名前にすることができます。

このクラスでは、すべてのマッピングを維持します。

ここに画像の説明を入力してください

ステップ4.スタートアップ「ConfigureServices」でマッパーを初期化する

スタートアップクラスでは、作成したプロファイルを初期化し、AutoMapperサービスを登録する必要があります。

  Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());

  services.AddAutoMapper();

AutoMapperを初期化および登録する必要があるConfigureServicesメソッドを示すコードスニペット。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });


        // Start Registering and Initializing AutoMapper

        Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
        services.AddAutoMapper();

        // End Registering and Initializing AutoMapper

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }}

ステップ5.出力を取得します。

マップされた結果を取得するには、AutoMapper.Mapper.Mapを呼び出し、適切な宛先とソースを渡す必要があります。

AutoMapper.Mapper.Map<Destination>(source);

コードスニペット

    [HttpPost]
    public void Post([FromBody] SchemeMasterViewModel schemeMaster)
    {
        if (ModelState.IsValid)
        {
            var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
        }
    }

13
次のエラーが表示されます'Mapper' does not contain a definition for 'initialize'AutoMapper.Extensions.Microsoft.DependencyInjectionバージョン7.0.0 を使用しています
kimbaudi

超詳細な答え。ありがとうございます。
Rod Hartzell、

1
ASP.NET CORE 3.0を使用している場合は、このチュートリアルを確認してください。ASP.NETCore 3.0でAutoMapper
Saineshwar

44

私は@theutzの答えを拡張したいと思います-つまりこの行

// services.AddAutoMapper(typeof(Startup));  // <-- newer automapper version uses this signature.

AutoMapper.Extensions.Microsoft.DependencyInjectionバージョン3.2.0にはバグ(おそらく)があります。(私は.NET Core 2.0を使用しています)

これは、この GitHubの問題で対処されています。AutoMapperのプロファイルクラスを継承するクラスが、スタートアップクラスであるアセンブリの外部に存在する場合、AutoMapperインジェクションが次のようになっていると、おそらく登録されません。

services.AddAutoMapper();

AutoMapperプロファイルを検索するアセンブリを明示的に指定しない限り。

Startup.ConfigureServicesで次のように実行できます。

services.AddAutoMapper(<assembies> or <type_in_assemblies>);

どこ「アセンブリ」「type_in_assemblies」アプリケーション内のプロファイルのクラスが指定されているアセンブリをポイントします。例えば:

services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));

私は仮定パラメータなしのオーバーロード(からソースコードの実装、次によること(と私は、この言葉に重点を置く)GitHubのを):

public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
     return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}

AutoMapperプロファイルが含まれているJIT済みのアセンブリがCLRに依存しています。AutoMapperプロファイルは必要な場合にのみjitされるため、trueまたはtrueでない場合があります(この StackOverflowの質問の詳細)。


5
AutoMapperとAspNetCoreの最新バージョンに対する正しい答えは、
Joshitです。

1
これが私がAutoMapper 8.1(最新バージョン)を探していた答えでした
Tinaira

30

ここでのtheutzの答えは非常に優れています。これを追加したいだけです。

MapperConfigurationExpression代わりにマッピングプロファイルを継承させる場合はProfile、非常に簡単にテストを追加してマッピング設定を確認できます。これは常に便利です。

[Fact]
public void MappingProfile_VerifyMappings()
{
    var mappingProfile = new MappingProfile();

    var config = new MapperConfiguration(mappingProfile);
    var mapper = new Mapper(config);

    (mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}

「AutoMapper Extension Dependencyインジェクションはasp.net core 1.1と互換性がありません」という1つのエラーが発生します。助けてください!
Rohit Arora 2017

「検証」の定義は議論の余地があるようです。これは、マッピングを防ぐために設計によって特定のプロパティが省略されている場合に爆発します。
Jeremy Holovacs

2
プロパティをマップしたくない場合は、.Ignore()で設定します。そうすることで、各ケースの処理について積極的に考えることを強いられます-変更が行われているときにものを見逃さないようにしてください。超実用的です。したがって、はい、検証テストは多くの人が理解するよりも大きなセーフティネットです。それは絶対確実ではありませんが、最初の90%を処理します。
Arve Systad

18

.NET Core 2.2 / Automapper 8.1.1 w / Extensions.DI 6.1.1の場合、この方法で解決しました(上記と同様ですが、よりクリーンなソリューションのように感じます)。

MappingProfile.csクラスを作成し、コンストラクターにマップを設定します(すべてのマッピングを保持するために単一のクラスを使用する予定です)

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Source, Dest>().ReverseMap();
        }
    }

Startup.csで、以下を追加してDIに追加します(アセンブリの引数は、マッピング構成を保持するクラス用です。私の場合は、MappingProfileクラスです)。

//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));

コントローラーでは、他のDIオブジェクトと同じように使用します

    [Route("api/[controller]")]
    [ApiController]
    public class AnyController : ControllerBase
    {
        private readonly IMapper _mapper;

        public AnyController(IMapper mapper)
        {
            _mapper = mapper;
        }

        public IActionResult Get(int id)
        {
            var entity = repository.Get(id);
            var dto = _mapper.Map<Dest>(entity);

            return Ok(dto);
        }
    }


2
私はあなたの答えが好きです。私はラップだと思うMappingProfilesnew Type[]{}に示すように、この答えは不要です。
ファットマンノーネック

10

私のStartup.cs(Core 2.2、Automapper 8.1.1)

services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });            

私のデータアクセスプロジェクト

namespace DAL
{
    public class MapperProfile : Profile
    {
        // place holder for AddAutoMapper (to bring in the DAL assembly)
    }
}

私のモデル定義では

namespace DAL.Models
{
    public class PositionProfile : Profile
    {
        public PositionProfile()
        {
            CreateMap<Position, PositionDto_v1>();
        }
    }

    public class Position
    {
        ...
    }

services.AddAutoMapper( typeof(DAL.MapperProfile) ); 代わりに 使用しないのですservices.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });か?
ファットマンノーネック

8

私はたくさんの答え、特に@saineshwarの答えが好きです。AutoMapper 9.0で.net Core 3.0を使用しているので、答えを更新するときがきたと感じています。

私のために働いたのは、Startup.ConfigureServices(...)でこの方法でサービスを登録しました:

    services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(), 
                               AppDomain.CurrentDomain.GetAssemblies());

@saineshwarの残りの回答は完璧に保たれていると思います。しかし、誰かが興味を持っている場合、私のコントローラーコードは次のとおりです:

[HttpGet("{id}")]
public async Task<ActionResult> GetIic(int id)
{
    // _context is a DB provider
    var Iic = await _context.Find(id).ConfigureAwait(false);

    if (Iic == null)
    {
        return NotFound();
    }

    var map = _mapper.Map<IicVM>(Iic);

    return Ok(map);
}

そして私のマッピングクラス:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Iic, IicVM>()
            .ForMember(dest => dest.DepartmentName, o => o.MapFrom(src => src.Department.Name))
            .ForMember(dest => dest.PortfolioTypeName, o => o.MapFrom(src => src.PortfolioType.Name));
            //.ReverseMap();
    }
}

-----編集-----

Lucian Bargaoanuのコメントにリンクされているドキュメントを読んだ後、この回答を少し変更することをお勧めします。

パラメータservices.AddAutoMapper()レス(@saineshwarの答えがあった)は(少なくとも私にとっては)機能しません。ただし、NuGetアセンブリAutoMapper.Extensions.Microsoft.DependencyInjectionを使用する場合、フレームワークはAutoMapper.Profileを拡張するすべてのクラス(mine、MappingProfileなど)を検査できます。

したがって、私の場合、クラスが同じ実行アセンブリに属している場合は、サービス登録を以下のように短縮できますservices.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(より洗練されたアプローチは、このコーディングでパラメーターのない拡張である可能性があります)。

ありがとう、ルシアン!



6

AutoMapper 6.1.1とasp.net Core 1.1.2を使用しています。

まず、Automapperのプロファイルクラスによって継承されるプロファイルクラスを定義します。空のIProfileインターフェイスを作成しました。このタイプのクラスを見つけることだけが目的です。

 public class UserProfile : Profile, IProfile
    {
        public UserProfile()
        {
            CreateMap<User, UserModel>();
            CreateMap<UserModel, User>();
        }
    }

次に、マッピングなどの別のクラスを作成します

 public class Mappings
    {
     public static void RegisterMappings()
     {            
       var all =
       Assembly
          .GetEntryAssembly()
          .GetReferencedAssemblies()
          .Select(Assembly.Load)
          .SelectMany(x => x.DefinedTypes)
          .Where(type => typeof(IProfile).GetTypeInfo().IsAssignableFrom(type.AsType()));

            foreach (var ti in all)
            {
                var t = ti.AsType();
                if (t.Equals(typeof(IProfile)))
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfiles(t); // Initialise each Profile classe
                    });
                }
            }         
        }

    }

Startup.csファイルのMVC Core web Projectのコンストラクターで、アプリケーションのロード時にすべてのマッピングを初期化するMappingクラスを呼び出します。

Mappings.RegisterMappings();

プロファイルクラスからサブクラスを作成するだけで、プログラムがservices.AddAutoMapper();を実行しています。コードの行オートマッパーはそれらを自動的に認識します。
isaeid 2017年

nugetで利用可能なAutoMapper.Extensions.Microsoft.DependancyInjectionを使用する場合、これは必要ないと思います。
グレッグガム

5

ASP.NET Core(2.0+および3.0を使用してテスト済み)の場合、ソースドキュメントを読みたい場合:https : //github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

それ以外の場合は、次の4つの手順に従ってください。

  1. nugetからAutoMapper.Extensions.Microsoft.DependancyInjectionをインストールします。

  2. いくつかのプロファイルクラスを追加するだけです。

  3. 次に、startup.csクラスに以下を追加します。 services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. 次に、コントローラーまたは必要な場所にIMapperを挿入するだけです。

public class EmployeesController {

    private readonly IMapper _mapper;

    public EmployeesController(IMapper mapper){

        _mapper = mapper;
    }

そして、ProjectToを今すぐ使用したい場合は、次のようにします。

var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()

4

AutoMapper 9.0.0の場合:

public static IEnumerable<Type> GetAutoMapperProfilesFromAllAssemblies()
    {
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var aType in assembly.GetTypes())
            {
                if (aType.IsClass && !aType.IsAbstract && aType.IsSubclassOf(typeof(Profile)))
                    yield return aType;
            }
        }
    }

MapperProfile:

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    CreateMap<Foo, FooDto>();
    // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
  }
}

あなたのスタートアップでは:

services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
            .ToArray());

コントローラーまたはサービス:マッパーを挿入:

private readonly IMapper _mapper;

使用法:

var obj = _mapper.Map<TDest>(sourceObject);

4

asp.netコアの最新バージョンでは、次の初期化を使用する必要があります。

services.AddAutoMapper(typeof(YourMappingProfileClass));

2

Asp.Net Core 2.2とAutoMapper.Extensions.Microsoft.DependencyInjection。

public class MappingProfile : Profile
{
  public MappingProfile()
  {
      CreateMap<Domain, DomainDto>();
  }
}

Startup.cs

services.AddAutoMapper(typeof(List.Handler));

1

テストのためにArve Systadが述べたことに追加します。なんらかの理由で私のようであり、theutzソリューションで提供される継承構造を維持したい場合は、次のようにMapperConfigurationを設定できます。

var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);

これはNUnitで行いました。


1

services.AddAutoMapper(); うまくいきませんでした。(私はAsp.Net Core 2.0を使用しています)

以下のように設定した後

   var config = new AutoMapper.MapperConfiguration(cfg =>
   {                 
       cfg.CreateMap<ClientCustomer, Models.Customer>();
   });

マッパーを初期化しますIMapper mapper = config.CreateMapper();

マッパーオブジェクトをシングルトンサービスとしてサービスに追加します。AddSingleton(mapper);

このようにして、コントローラーにDIを追加できます

  private IMapper autoMapper = null;

  public VerifyController(IMapper mapper)
  {              
   autoMapper = mapper;  
  }

そして、私は私の行動方法で以下のように使用しました

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);

こんにちは@venkatこんにちは、おそらくAutoMapper.Extensions.Microsoft.DependancyInjectionパッケージをプロジェクトに追加する必要があるだけです
dalcam

-1

theutz answerについては、コントローラーコンストラクターでIMapperマッパーパラメーターを指定する必要はありません。

マッパーはコードの任意の場所で静的メンバーであるため、使用できます。

public class UserController : Controller {
   public someMethod()
   {
      Mapper.Map<User, UserDto>(user);
   }
}

11
しかし、静力学は少し反テスト可能です、いいえ?
スコットフレーリー2017年

3
うん。これは多くの場合に機能しますが、テストでこのメソッドを呼び出すときに構成済みのマッピングがない場合、例外がスローされます(したがって、間違った理由でテストが失敗します)。インジェクションIMapperすると、それをモックして、たとえば、指定されたテストに関係がない場合はnullを返すようにすることができます。
Arve Systad
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.