3

NHibernate と AutoMapper を使用して DTO のエンティティ ID をドメインのエンティティに変換することに関する StackOverflow の投稿を読んでいます。そこには確かに多くの情報がありますが、誰もが異なる提案をしているようで、その多くは別のツール ( ValueInjecter ) を使用することを提案しています。さらに、私が見つけた情報の多くは、数年前のものです。そのため、問題を解決することを期待して、この質問に再度取り組みます。

次の DTO クラスがあります。

public class PlaylistDto
{
    public Guid Id { get; set;
    public Guid StreamId { get; set; }
    public List<PlaylistItemDto> Items { get; set; }
}

および対応するドメイン:

public class Playlist
{
    public Guid Id { get; set;
    public Stream Stream { get; set; }
    //  Use interfaces so NHibernate can inject with its own collection implementation.
    public IList<PlaylistItem> Items { get; set; }
}

まず、これら 2 つのエンティティを相互にマップする意図を宣言します。

Mapper.CreateMap<Playlist, PlaylistDto>().ReverseMap();
Mapper.CreateMap<PlaylistItem, PlaylistItemDto>().ReverseMap();
Mapper.CreateMap<Stream, StreamDto>().ReverseMap();

ReverseMap を使用すると、双方向のマッピングを簡単に宣言できます。

この時点で、多くの労力を費やすことなく、Playlist を PlaylistDto に正常に変換できます。

//  Singular:
PlaylistDto playlistDto = Mapper.Map<Playlist, PlaylistDto>(playlist);

//  Collection:
List<PlaylistDto> playlistDtos = Mapper.Map<List<Playlist>, List<PlaylistDto>>(playlists);

これはうまくいきます。追加のコードは必要ありません。ただし、別の方向をマッピングしようとすると問題が発生します。

プレイリストDto は、そのストリームへの ID 参照のみを保存します。DTO をドメインに変換すると、次のようになります。

Playlist playlist = Mapper.Map<PlaylistDto, Playlist>(playlistDto);

この場合、プレイリストの Stream は、playlistDto の StreamID に関係なく、常に null です。

Dto の entityId を使用して NHibernate 経由でドメインのエンティティを取得できるようにする中間ステップを追加したいと思います。

私は AutoMapper を使用していませんでした。次の方法でこれを実現します。

playlist.Stream = StreamDao.Get(playlistDto.StreamId);

そうは言っても、私は質問があります:

  • AutoMapper を使用してこれを達成するための合意された最も簡単な方法は何ですか?
  • ValueInjecter は本当にここで検討すべき選択肢ですか? AutoMapper に頭痛の種になることを強制する道を進んでいますか?
  • ValueInjecter が優先される場合...それはまだ維持されていますか? プロジェクトは非常に時代遅れに見えます。さらに、ValueInjecter はコレクションをサポートしていないという言及も見ました。これが事実である場合、これは大きなターンオフになります。

私が見たいくつかの例は、私の問題を解決する可能性があります:

AutoMapper を使用して DTO を平坦化しない:

Mapper.CreateMap<Person, Domain.Person>()
    .ForMember(dest => dest.Address, opt => opt.ResolveUsing( src => { return new Address() {Address1 = src.Address, City = src.City, State = src.State }; }))

AutoMapper は IdPost を Post にマップします

public class Id2EntityConverter<TEntity> : ITypeConverter<int, TEntity> where TEntity : EntityBase
{
    public Id2EntityConverter()
    {
        Repository = ObjectFactory.GetInstance<Repository<TEntity>>();
    }

    private IRepository<TEntity> Repository { get; set; }

    public TEntity ConvertToEntity(int id)
    {
        var toReturn = Repository.Get(id);
        return toReturn;
    }

    #region Implementation of ITypeConverter<int,TEntity>

    public TEntity Convert(ResolutionContext context)
    {
        return ConvertToEntity((int)context.SourceValue);
    }

    #endregion
}

(there's more to this, but this is the gist of it)

アドバイスをいただければ幸いです。ありがとう!

4

3 に答える 3

1

これが私の現在の解決策です。Id2Entity コンバーターよりもこれを好むと思います。これは、デバッグがはるかに簡単である/あまり賢くないように思われるからです。賢くないこともありますが、よりデバッグしやすいことが重要です。

手動ではなく簡単にこれを行う方法について誰かがアイデアを持っている場合は、ぜひ聞いてください。

多くのプロパティを無視せずに AssertConfigurationIsValid() を呼び出すことができないため、AfterMap() を使用しないでください。ForMemeber 内で呼び出した方がよいでしょう。

/// <summary>
///     Initialize the AutoMapper mappings for the solution.
///     http://automapper.codeplex.com/
/// </summary>
private static void CreateAutoMapperMaps()
{
    AutofacRegistrations.RegisterDaoFactory();
    ILifetimeScope scope = AutofacRegistrations.Container.BeginLifetimeScope();
    var daoFactory = scope.Resolve<IDaoFactory>();

    Mapper.CreateMap<Error, ErrorDto>()
          .ReverseMap();

    IPlaylistItemDao playlistItemDao = daoFactory.GetPlaylistItemDao();
    IPlaylistDao playlistDao = daoFactory.GetPlaylistDao();
    IStreamDao streamDao = daoFactory.GetStreamDao();
    IUserDao userDao = daoFactory.GetUserDao();

    Mapper.CreateMap<Playlist, PlaylistDto>()
          .ReverseMap()
          .ForMember(playlist => playlist.FirstItem,
                     opt => opt.MapFrom(playlistDto => playlistItemDao.Get(playlistDto.FirstItemId)))
          .ForMember(playlist => playlist.NextPlaylist,
                     opt => opt.MapFrom(playlistDto => playlistDao.Get(playlistDto.NextPlaylistId)))
          .ForMember(playlist => playlist.PreviousPlaylist,
                     opt => opt.MapFrom(playlistDto => playlistDao.Get(playlistDto.PreviousPlaylistId)))
          .ForMember(playlist => playlist.Stream,
                     opt => opt.MapFrom(playlistDto => streamDao.Get(playlistDto.StreamId)));

    Mapper.CreateMap<PlaylistItem, PlaylistItemDto>()
          .ReverseMap()
          .ForMember(playlistItem => playlistItem.NextItem,
                     opt => opt.MapFrom(playlistItemDto => playlistItemDao.Get(playlistItemDto.NextItemId)))
          .ForMember(playlistItem => playlistItem.PreviousItem,
                     opt => opt.MapFrom(playlistItemDto => playlistItemDao.Get(playlistItemDto.PreviousItemId)))
          .ForMember(playlistItem => playlistItem.Playlist,
                     opt => opt.MapFrom(playlistItemDto => playlistDao.Get(playlistItemDto.PlaylistId)));

    Mapper.CreateMap<ShareCode, ShareCodeDto>().ReverseMap();

    Mapper.CreateMap<Stream, StreamDto>()
          .ReverseMap()
          .ForMember(stream => stream.User,
                     opt => opt.MapFrom(streamDto => userDao.Get(streamDto.UserId)))
          .ForMember(stream => stream.FirstPlaylist,
                     opt => opt.MapFrom(streamDto => playlistDao.Get(streamDto.FirstPlaylistId)));

    Mapper.CreateMap<User, UserDto>().ReverseMap();
    Mapper.CreateMap<Video, VideoDto>().ReverseMap();

    Mapper.AssertConfigurationIsValid();
}
于 2013-07-04T17:27:48.207 に答える