Как сделать, чтобы NHibernate 5 ConventionModelMapper отображал ICollection как установленный по умолчанию - PullRequest
0 голосов
/ 07 ноября 2019

У меня написано несколько существующих моделей, и я пытался сопоставить их с NHibernate (версия 5). Как оказалось, это свойства, представленные как IEnumerable<T> с полями поддержки, объявленными как ICollection<T>, например:

    public class Encounter
    {
        public virtual String Title { get; protected set; } = null!;
        public virtual IEnumerable<Conversation> Conversations => _conversations.AsEnumerable();
        private ICollection<Conversation> _conversations = new HashSet<Conversation>();
        protected Encounter() { }
        // ...
    }

Я пытаюсь использовать ConventionModelMapper как можно больше, и покатак хорошо, но я обнаружил, что он не знает, что делать с ICollection<T>, и я получаю TransientObjectException s, когда он пытается сохранить объекты в этих коллекциях. Если я поменяю их на ISet<T> s, то по правилам все будет нормально ... но похоже, что это должно быть то, что я должен быть в состоянии легко сказать ConventionModelMapper, так как у меня есть несколько таких ICollection<T> участники.

Я не нахожу легкий путь ... Это часть пути, но он переопределяет существующую логику IsSet(), поэтому мне пришлось бы добавить обратно условия для ISet членов впо крайней мере, и это не работает для моих IEnumerable<T> свойств с полями поддержки (с которыми NH явно способен иметь дело, потому что он правильно подбирает ISet поля поддержки) ...

    var mapper = new ConventionModelMapper();
    mapper.IsSet((memberInfo, b1) =>
    {
        var memberType = memberInfo.GetPropertyOrFieldType();
        if (memberType.IsGenericType)
        {
            return memberType.GetGenericInterfaceTypeDefinitions().Contains(typeof(ICollection<>));
        }
        return false;
    });

Кажетсякак это должно быть проще, я пропускаю ловушку событий?

1 Ответ

0 голосов
/ 12 ноября 2019

После некоторых дальнейших экспериментов и исследований, я пришел к выводу, что это работает ... но это кажется перегруженным и не очень СУХИМЫМ. Прежде чем я проведу рефакторинг, кто-нибудь скажет мне, что есть лучший способ?

// All this just to get NHibernate to map ICollection as "Set" by default?
mapper.IsSet((memberInfo, b1) =>
{
    IEnumerable<Type> typeDefs;
    Type memberType = memberInfo.GetPropertyOrFieldType();
    if (memberType.IsGenericType)
    {
        typeDefs = memberType.GetGenericInterfaceTypeDefinitions();
        if(typeDefs.Contains(typeof(ISet<>)) || typeDefs.Contains(typeof(ICollection<>)))
        {
            return true;
        }
    }
    const BindingFlags defaultBinding = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
    var fieldInfo = (from ps in PropertyToField.DefaultStrategies.Values
        let fi = memberInfo.DeclaringType.GetField(ps.GetFieldName(memberInfo.Name), defaultBinding)
        where fi != null
        select fi).FirstOrDefault();
    if (fieldInfo != null)
    {
        memberType = fieldInfo.GetPropertyOrFieldType();
        if (memberType.IsGenericType)
        {
            typeDefs = memberType.GetGenericInterfaceTypeDefinitions();
            if (typeDefs.Contains(typeof(ISet<>)) || typeDefs.Contains(typeof(ICollection<>)))
                return true;
        }
    }
    return false;
});

Это почти копия пасты и использует код из NHibernate и включенный в него PropertyToField служебный класс. Я в основном понимаю это, но это просто ... многословно ... кажется, что должен быть лучший вариант. Опять же, я где-то пропускаю простой крючок?

...