#c# #automapper
#c# #automapper
Вопрос:
Я настраиваю профиль automapper для сопоставления элемента Revit (из их api) с каким-либо пользовательским элементом. Их API получил циклические ссылки (1 элемент => n параметров, 1 параметр => 1 элемент) Я вызываю методы PreserveReference() . Но, похоже, это не работает, потому что у меня есть исключение StackOverflowException. Итак, мне интересно, как работает PreserveReference? Могу ли я указать свойство для проверки равенства вместо использования ссылок?
public class Element
{
public int Id { get; set; }
public List<Parameter> Parameters { get; set; }
public override bool Equals(object obj)
{
return obj is Element element amp;amp; Id == element.Id;
}
public override int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return Id;
}
}
public class Parameter
{
public int Id { get; set; }
public Definition Definition { get; set; }
public Element Element { get; set; }
}
profile.CreateMap<Element, Models.Element>()
.ForMember(element => element.Id, expression => expression.MapFrom(element => element.Id.IntegerValue))
.IncludeAllDerived()
.PreserveReferences();
profile.CreateMap<Parameter, Models.Parameter>()
.ForMember(parameter => parameter.Id, expression => expression.MapFrom(parameter => parameter.Id.IntegerValue))
.IncludeAllDerived()
.PreserveReferences();
Комментарии:
1. Начиная с версии 6.1.0 PreserveReferences устанавливается автоматически во время настройки, поэтому устанавливать его нет необходимости. Обновите до последней версии. Если это не сработает, сформулируйте суть , которую мы можем выполнить и увидеть сбой.
2. Даже если я установлю его или нет, он все равно не сработает. Revit API сделан так, что мне нужно сообщить AutoMapper сохранить ссылку на основе свойства Id: revitapidocs.com/2019/671c33f6-169b-17ca-583b-42f9df50ace5.htm
3. Нет, идея в том, что у вас этого не получится. Чего бы ни хотел этот API, это другое. У AM такого нет. AM.Collections делает что-то похожее, поэтому вы можете попробовать это.
4. Я все еще получаю ЭТО. Это довольно логично, потому что с помощью Revit API два элемента с одинаковым идентификатором могут быть не равны.
5. Если у вас ЭТО получится во время составления карты, дайте мне представление, и я посмотрю.
Ответ №1:
Я нахожу способ получить то, что я хочу. Я должен был получить доступ к такой частной собственности, как эта :
public static T GetSource<T>(this ContextCacheKey contextCacheKey)
{
var source = (T)typeof(ContextCacheKey).GetField("_source", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance)?.GetValue(contextCacheKey);
return source;
}
public class ElementConverter : IValueConverter<Element, Models.Element>
{
public Models.Element Convert(Element sourceMember, ResolutionContext context)
{
var a = (Models.Element)context.InstanceCache.FirstOrDefault(kvp => kvp.Key.GetSource<Element>().Id.Equals(sourceMember.Id)).Value ?? context.Mapper.Map<Models.Element>(sourceMember);
return a;
}
}
Простым решением было бы иметь Source и destinationType public в структуре ContextCacheKey :
public struct ContextCacheKey : IEquatable<ContextCacheKey>
{
public static bool operator ==(ContextCacheKey left, ContextCacheKey right) => left.Equals(right);
public static bool operator !=(ContextCacheKey left, ContextCacheKey right) => !left.Equals(right);
public readonly object _source;
public readonly Type _destinationType;
public ContextCacheKey(object source, Type destinationType)
{
_source = source;
_destinationType = destinationType;
}
public override int GetHashCode() => HashCodeCombiner.Combine(_source, _destinationType);
public bool Equals(ContextCacheKey other) =>
_source == other._source amp;amp; _destinationType == other._destinationType;
public override bool Equals(object other) =>
other is ContextCacheKey amp;amp; Equals((ContextCacheKey)other);
}
Комментарии:
1. Это просто глупо 🙂 Почему бы не иметь свой собственный словарь и не использовать его так, как вы хотите?
2. Кажется, немного сложнее иметь свои собственные словари для каждого типа, чтобы хранить все во время карты и очищать после завершения карты. Прямо сейчас я даже не знаю, как я мог бы это сделать :/
3. У вас есть контекст. Предметы, и вы можете хранить там все, что угодно.
4. Я попробовал IValueConverter с пользовательским словарем внутри в качестве кэша. Но если словарь пуст или элемент еще не был сопоставлен, я должен вызвать Context.Mapper.Map<T>(sourceMember). Который рано или поздно вызовет, из-за циклических ссылок, ValueConverter , который проверит словарь, который все еще пуст, поэтому он снова вызовет функцию Map и т. Д… Затем так снова.
5. Проверьте план выполнения . Вы можете увидеть, как AM это делает, и попытаться подражать этому.