【问题标题】:Converting a DTO's entityID to a Domain's entity through NHibernate and AutoMapper通过 NHibernate 和 AutoMapper 将 DTO 的 entityID 转换为 Domain 的实体
【发布时间】:2013-07-03 16:46:41
【问题描述】:

我一直在阅读有关使用 NHibernate 和 AutoMapper 将 DTO 的 entityID 转换为域的实体的 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 允许我轻松声明双向映射。

此时,我可以毫不费力地将 Playlist 成功转换为 PlaylistDto:

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

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

这很好用。不需要额外的代码。但是,当我尝试绘制另一个方向时,就会出现问题。

playlistDto 仅存储对其 Stream 的 ID 引用。如果我将 DTO 转换为域,如下所示:

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

那么无论 playlistDto 的 StreamID 是什么,播放列表的 Stream 始终为 null。

我想添加一个中间步骤,允许使用 Dto 的 entityId 通过 NHibernate 获取域的实体。

我没有使用 AutoMapper,我会通过以下方式实现:

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

话虽如此,我有问题:

  • 商定使用 AutoMapper 实现此目的的最简单方法是什么?
  • ValueInjecter 真的是我应该考虑的选择吗?我是否会走上强迫 AutoMapper 做会导致头痛的事情的道路?
  • 如果 ValueInjecter 是首选...是否仍然维护?该项目看起来非常过时。另外,我看到有人提到 ValueInjecter 不支持集合。如果是这样的话,这将是一个巨大的转变。

我看到的一些可能解决我的问题的示例:

Using AutoMapper to unflatten a 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 map IdPost to 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)

建议赞赏。谢谢!

【问题讨论】:

    标签: c# nhibernate automapper dto


    【解决方案1】:

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

    【讨论】:

    • 当然。我有兴趣查看完整的代码。请注意,我的 ID 是 GUID 而不是 int。假设对 int 没有内在依赖,我可以进行适当的转换。
    • @SeanAnderson:我添加了扫描程序集并创建映射的类。
    【解决方案2】:

    这是我目前的解决方案。我认为我更喜欢它而不是 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();
    }
    

    【讨论】:

      【解决方案3】:

      您缺少(应用程序)服务层(由客户端/表示层调用的那个)。

      该层接收请求(例如通过电子邮件获取)。

      然后它从域服务中获取域模型/实体并将其转换为 DTO;使用 DTOprovider 并在响应消息中将其发送回客户端,并将 DTO 作为内容的一部分。

      同一层在另一个请求(例如保存)中接收到 dto,它使用一个或多个域服务重构域模型/实体,验证要持久化的模型,如果有效,则使用保存域模型其相应的域服务并返回带有成功结果的响应,可能还有一个新的 ID; else if invalid 返回验证结果失败。 (返回 dto 是可选的...)

      从此链接查看图层: http://msdn.microsoft.com/en-us/library/ee658109.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-18
        • 2016-06-10
        • 1970-01-01
        相关资源
        最近更新 更多