Automapper将IList<>映射到Iesi.Collections.Generic.ISet<>

4

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

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)的映射运行良好,符合预期,我得到了一个包含“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]

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

1个回答

2

这是因为AutoMapper目前不支持ISet<>集合属性。当ISet<>的目标属性已经实例化(不为null)时,它可以工作,因为ISet<>实际上继承自ICollection<>,因此Automapper可以理解并正确地进行集合映射。

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

有三种解决方法:

  1. 创建一个功能请求来支持ISet<>集合,并希望他们会添加它。
  2. 确保属性不为空。例如,在构造函数中将其实例化为空的HashSet<>。这可能会对ORM层造成一些麻烦,但是可以做到。
  3. 我采用的最佳解决方案是创建自定义值解析器,你已经有了并在必要时实例化该属性。您需要实现IValueResolver,因为提供的基本ValueResolver不会让您实例化该属性。以下是我使用的代码片段:
公共类EntityCollectionMerge:IValueResolver,其中TDest:IEntityWithId,TSource:IDtoWithId { public ResolutionResult Resolve(ResolutionResult source) { //如果源集合不可枚举,则返回 var sourceCollection = source.Value as IEnumerable; if (sourceCollection == null) return source.New(null, typeof(IEnumerable));
//如果目标集合是ISet if (typeof(ISet).IsAssignableFrom(source.Context.DestinationType)) { //获取目标ISet var destSet = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ISet; //如果目标集合为null,则实例化它 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)) { //获取目标集合 var destCollection = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ICollection; //如果目标集合为null,则实例化它 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);
//添加新数据或更新 foreach (var sourceDto in sourceDtos) { //如果源不存在于目标集合中,请添加它 if (!destinationIds.Contains(sourceDto.Key)) { destination.Add(sourceDto.Value); continue; }
//更新已存在的数据 Mapper.Map(sourceDto.Value, destination.First(x => x.Id == sourceDto.Key)); }
//删除从源dto中删除的目标实体 foreach (var entityToDelete in destination.Where(entity => !sourceDtos.ContainsKey(entity.Id)).ToList()) { destination.Remove(entityToDelete); } } }
然后在您的映射中使用opt => opt.ResolveUsing(new EntitCollectionMerge<Entity,Dto>()).FromMember(x => x.ISetMember),如果您有很多这样的集合,则可以通过typeMaps自动将它们添加到所有集合中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接