簡體   English   中英

通過NHibernate和AutoMapper將DTO的entityID轉換為Domain的實體

[英]Converting a DTO's entityID to a Domain's entity through NHibernate and AutoMapper

我一直在閱讀有關使用NHibernate和AutoMapper將DTO的entityID轉換為Domain的實體的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; }
}

首先,我聲明我打算將這兩個實體相互映射:

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

ReverseMap允許我輕松聲明雙向映射。

此時,我可以毫不費力地將播放列表成功轉換為PlaylistDto:

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

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

這非常有效。 不需要額外的代碼。 但是,當我嘗試映射另一個方向時會出現問題。

播放列表Dto僅存儲其Stream的ID引用。 如果我將DTO轉換為域,如下所示:

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

無論playlistDto的StreamID如何,播放列表的Stream始終為null。

我想添加一個中間步驟,允許使用Dto的entityId通過NHibernate獲取Domain的實體。

我沒有使用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)

建議表示贊賞。 謝謝!

Id2Entity轉換器是我們在一個非常大的項目中廣泛使用的,它可以完美地工作。 這里的訣竅是你掃描所有實體並設置從int到你的類型的映射。 如果您需要完整的代碼,請告訴我。

這是創建映射的類。

public class AutoMapperGlobalConfiguration : IGlobalConfiguration
    {
        private readonly IConfiguration _configuration;

        public AutoMapperGlobalConfiguration(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        private void RegisterAssembly(Assembly assembly)
        {
            //add all defined profiles
            var query = assembly.GetExportedTypes()
                .Where(x => x.CanBeCastTo(typeof(Profile)));

            foreach (Type type in query)
            {
                var profile = ObjectFactory.GetInstance(type).As<Profile>();
                _configuration.AddProfile(profile);


                Mapper.AddProfile(profile);

            }
        }

        public void Configure()
        {
            _configuration.RecognizePostfixes("Id");

            var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("DM."));

            //create maps for all Id2Entity converters
            MapAllEntities(_configuration);

            assemblies.Each(RegisterAssembly);
        }

        private static void MapAllEntities(IProfileExpression configuration)
        {
            //get all types from the domain assembly and create maps that
            //convert int -> instance of the type using Id2EntityConverter
            var openType = typeof(Id2EntityConverter<>);
            var idType = typeof(int);

            var persistentEntties = typeof(Domain.Entities).Assembly.GetTypes()
               .Where(t => typeof(EntityBase).IsAssignableFrom(t))
               .Select(t => new
               {
                   EntityType = t,
                   ConverterType = openType.MakeGenericType(t)
               });
            foreach (var e in persistentEntties)
            {
                var map = configuration.CreateMap(idType, e.EntityType);
                map.ConvertUsing(e.ConverterType);
            }
     }
}

這是我目前的解決方案。 我認為我比Id2Entity轉換器更喜歡這個,因為這似乎更容易調試/不那么聰明。 有時候不那么聰明,但更多的可調試性是關鍵。

如果有人知道如何簡單地做到這一點,而不是手動,我很樂意聽到它。

不要使用AfterMap()因為那樣你就不能在不忽略很多屬性的情況下調用AssertConfigurationIsValid()。 最好只調用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();
}

您缺少(應用程序)服務層(客戶端/表示層調用的服務層)。

該層接收請求(例如通過電子郵件獲取)。

然后,它從域服務中獲取域模型/實體並將其轉換為DTO; 使用DTO提供者並在響應消息中將其發送回客戶端,並將DTO作為內容的一部分。

同一層在另一個請求中接收dto(例如保存),它使用一個或多個域服務重建域模型/實體,驗證要保留的模型,如果有效,則使用其對應的域保存域模型服務並返回響應,結果是成功,也許是新的ID; 否則,如果無效則返回驗證結果失敗。 (返回dto是可選的......)

查看此鏈接中的圖層: http//msdn.microsoft.com/en-us/library/ee658109.aspx

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM