【问题标题】:Automapper mapping IList<> to Iesi.Collections.Generic.ISet<>自动映射器将 IList<> 映射到 Iesi.Collections.Generic.ISet<>
【发布时间】:2012-01-10 14:17:03
【问题描述】:

我在标题中提到的映射中遇到了一些问题。以下是详细信息:

class MyDomain
{
   public Iesi.Collections.Generic.ISet<SomeType> MySomeTypes{ get; set; }
   ....
}


class MyDTO
{
  public IList<SomeTypeDTO> MySomeTypes{ get; set; }
  ...
}

映射:

Mapper.CreateMap<MyDomain, MyDTO>().ForMember(dto=>dto.MySomeTypes, opt.ResolveUsing<DomaintoDTOMySomeTypesResolver>());

Mapper.CreateMap<MyDTO, MyDomain>().ForMember(domain=>domain.MySomeTypes, opt.ResolveUsing<DTOtoDomainMySomeTypesResolver>());

解析器:

class DomaintoDTOMySomeTypesResolver: ValueResolver<MyDomain, IList<SomeTypeDTO>> 
{
  protected override IList<SomeTypeDTO> ResolveCore(MyDomain source)
  {
      IList<SomeTypeDTO> abc = new List<DemandClassConfigurationDTO>();
      //Do custom mapping
      return abc;
  }
}

class DTOtoDomainMySomeTypesResolver: ValueResolver<MyDTO, Iesi.Collections.Generic.ISet<SomeType>> 
{
   protected override Iesi.Collections.Generic.ISet<SomeType> ResolveCore(SystemParameterDTO source)
   {
     Iesi.Collections.Generic.ISet<SomeType> abc = new HashedSet<SomeType>();
     //Do custom mapping
     return abc;
   }
}

从域映射到 DTO 工作正常,正如预期的那样,我得到了一个带有 IList 的“SomeTypeDTO”对象的 MyDTO 对象。 但是,将 DTO 映射到域会引发以下错误:

 Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
  ----> AutoMapper.AutoMapperMappingException : Trying to map  Iesi.Collections.Generic.HashedSet`1[SomeType, MyAssembly...] to Iesi.Collections.Generic.ISet`1[SomeType, MyAssembly...]

 Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
  ----> System.InvalidCastException : Unable to cast object of type 'System.Collections.Generic.List`1[SomeType]' to type 'Iesi.Collections.Generic.ISet`1[SomeType]

我可能做错了什么以及错误消息意味着什么?几乎似乎自动映射器在映射 ISet(及其具体实现 HashedSet)时遇到了一些问题。我的理解是,在上述场景中,自动映射器应该只使用“DTOtoDomainMySomeTypesResolver”返回的 ISet 引用。我也不明白为什么会出现“从 List 转换为 ISet 错误”。

【问题讨论】:

    标签: automapper


    【解决方案1】:

    这是因为 AutoMapper 当前不支持 ISet&lt;&gt; 集合属性。它在ISet&lt;&gt; 的目标属性已经实例化(不为空)时起作用,因为ISet&lt;&gt; 实际上是从ICollection&lt;&gt; 继承的,因此 Automapper 可以理解这一点,并将正确地进行集合映射。

    当目标属性为空且为接口类型时不起作用。您会收到此错误,因为 automapper 实际上发现它可以从 ICollection&lt;&gt; 分配,因此它使用通用 List&lt;&gt; 实例化该属性,这是 automapper 必须创建新集合属性时的默认集合,但是当它尝试实际分配它时,它会失败,因为显然List&lt;&gt; 不能转换为ISet&lt;&gt;

    解决方法有以下三种:

    1. 创建一个功能请求以支持ISet&lt;&gt; 集合并希望他们添加它
    2. 确保该属性不为空。例如。在构造函数中将其实例化为空HashSet&lt;&gt;。这可能会给 ORM 层带来一些麻烦,但是是可行的
    3. 我采用的最佳解决方案是创建自定义值解析器,您已经拥有该解析器,如果该属性为空,请自行实例化该属性。您需要实现IValueResolver,因为提供的基础ValueResolver 不会让您实例化该属性。这是我使用的代码 sn-p:
    public class EntityCollectionMerge : IValueResolver where TDest : IEntityWithId where TSource : IDtoWithId { public ResolutionResult Resolve(ResolutionResult source) { //if source collection is not enumerable return var sourceCollection = source.Value as IEnumerable; if (sourceCollection == null) return source.New(null, typeof(IEnumerable)); //if the destination collection is ISet if (typeof(ISet).IsAssignableFrom(source.Context.DestinationType)) { //get the destination ISet var destSet = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ISet; //if destination set is null, instantiate it if (destSet == null) { destSet = new HashSet(); source.Context.PropertyMap.DestinationProperty.SetValue(source.Context.DestinationValue, destSet); } Merge(sourceCollection, destSet); return source.New(destSet); } if (typeof(ICollection).IsAssignableFrom(source.Context.DestinationType)) { //get the destination collection var destCollection = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ICollection; //if destination collection is null, instantiate it if (destCollection == null) { destCollection = new List(); source.Context.PropertyMap.DestinationProperty.SetValue(source.Context.DestinationValue, destCollection); } Merge(sourceCollection, destCollection); return source.New(destCollection); } throw new ArgumentException("Only ISet and ICollection are supported at the moment."); } public static void Merge(IEnumerable source, ICollection destination) { if (source == null) return; var destinationIds = destination.Select(x => x.Id).ToHashSet(); var sourceDtos = source.ToDictionary(x => x.Id); //add new or update foreach (var sourceDto in sourceDtos) { //if the source doesnt exist in destionation add it if (sourceDto.Key (sourceDto.Value)); continue; } //update exisiting one Mapper.Map(sourceDto.Value, destination.First(x => x.Id == sourceDto.Key)); } //delete entity in destination which were removed from source dto foreach (var entityToDelete in destination.Where(entity => !sourceDtos.ContainsKey(entity.Id)).ToList()) { destination.Remove(entityToDelete); } } }

    然后在您的映射中使用opt =&gt; opt.ResolveUsing(new EntitCollectionMerge&lt;Entity,Dto&gt;()).FromMember(x =&gt; x.ISetMember),或者如果您有很多这样的集合,您可以通过 typeMaps 将它们自动添加到所有集合中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-09
      相关资源
      最近更新 更多