gpt4 book ai didi

自动映射器将 IList<> 映射到 Iesi.Collections.Generic.ISet<>

转载 作者:行者123 更新时间:2023-12-02 04:04:38 27 4
gpt4 key购买 nike

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

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 目前不支持 ISet<>集合属性。它在 ISet<> 的目标属性时起作用已经实例化(不为空),因为 ISet<>实际上继承自 ICollection<> ,因此 Automapper 可以理解这一点,并将正确地进行集合映射。

当目标属性为 null 并且是接口(interface)类型时,它不起作用。你得到这个错误,因为 automapper 实际上发现它可以从 ICollection<> 分配。所以它使用通用 List<> 实例化属性,这是 automapper 必须创建新集合属性时的默认集合,但是当它尝试实际分配它时,它将失败,因为显然 List<>不能转换为 ISet<>
对此有三种解决方案:

  • 创建功能请求以支持 ISet<>收藏并希望他们会添加它
  • 确保该属性不为空。例如。在构造函数中将其实例化为空 HashSet<> .这可能会给 ORM 层带来一些麻烦,但可行
  • 我采用的最佳解决方案是创建自定义值解析器,如果它为空,您已经拥有并自己实例化该属性。您需要执行 IValueResolver , 因为提供的基数 ValueResolver不会让您实例化该属性。这是我使用的代码片段:


  • 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 => opt.ResolveUsing(new EntitCollectionMerge<Entity,Dto>()).FromMember(x => x.ISetMember)或者,如果您有很多这样的集合,您可以通过 typeMaps 将它们自动添加到所有集合中。

    关于自动映射器将 IList<> 映射到 Iesi.Collections.Generic.ISet<>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8341297/

    27 4 0