Это потому, что AutoMapper в настоящее время не поддерживает ISet<>
свойства коллекции.Он работает, когда свойство назначения ISet<>
уже создано (не равно нулю), потому что ISet<>
на самом деле наследует от ICollection<>
, поэтому Automapper может понять это и правильно выполнит сопоставление коллекции.
Это не работает, когда свойство destination имеет значение null и имеет тип интерфейса.Вы получаете эту ошибку, потому что automapper фактически обнаружил, что его можно назначить из ICollection<>
, поэтому он создает экземпляр свойства, используя generic 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.