Разработка пользовательского картографа PetaPoco, поддерживающего класс перечисления HeadSprings

Я пытаюсь создать сопоставитель, чтобы PetaPoco могла гидратировать и сохранять POCO со свойствами класса перечисления. Подробнее о классах перечисления см. здесь или здесь.

Например, возьмите этот класс.

    public class PetType : Headspring.Enumeration<PetType>
    {
       public static readonly PetType Frog = new PetType(1, "Frog");
       public static readonly PetType Cat = new PetType(2, "Cat");
       public static readonly PetType Fish = new PetType(3, "Fish");
       public static readonly PetType Dog = new PetType(4, "Dog");

       private PetType(int value, string displayName) : base(value, displayName) { }
     }

Что можно использовать так:

var MyPet = PetType.Dog;

Вот Poco, который я хочу гидратировать/сохранять с помощью базы данных:

    public class Pet
    {
         public int ID { get; set; }
         public string OwnerName { get; set; }
         public DateTime DateOfBirth { get; set; }
         public string PetName{ get; set; }
         public PetType PetType{ get; set; }
    }

Я разработал собственный маппер, который будет работать с PetType:

    class EnumClassMapper :  PetaPoco.StandardMapper 
    {
       public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
       {
           if (targetProperty.PropertyType == typeof(PetType))
           {
               return (x) => PetType.FromValue((int) x);
           }
           return base.GetFromDbConverter(targetProperty, sourceType);
       }

       public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
       {
           if (sourceProperty.PropertyType == typeof(PetType))
           {
               return (x) => ((PetType)x).Value;
           }
           return base.GetToDbConverter(sourceProperty);
       }
   }

Однако предположим, что я создаю еще один подкласс Enumeration для размещения.

    public class Disposition: Headspring.Enumeration<Disposition>
    {
       public static readonly Friendly = new Disposition(1, "Friendly");
       public static readonly Timid = new Disposition(2, "Timid");
       public static readonly Aggressive = new Disposition(3, "Aggressive");

       private Disposition(int value, string displayName) : base(value, displayName) { }
     }

Я не хочу обновлять свой преобразователь каждый раз, когда создаю новый подкласс класса Enumeration. Я предпочитаю, чтобы код отображения мог распознавать, что тип свойства является потомком класса Enumeration, и соответствующим образом отображать его. Я предполагаю, что ответ заключается в использовании отражения, но я не знаю, как поступить.


person Aheho    schedule 31.01.2016    source источник


Ответы (1)


Как насчет

public class EnumClassMapper<T> :  PetaPoco.StandardMapper 
    where T : Headspring.Enumeration<T>   
{
   public override Func<object, object> GetFromDbConverter(System.Reflection.PropertyInfo targetProperty, Type sourceType)
   {
       return (x) => Enumeration<T, int>.FromValue((int) x);
   }

   public override Func<object, object> GetToDbConverter(System.Reflection.PropertyInfo sourceProperty)
   {
       return (x) => ((T)x).Value;
   }
}

var builder = DatabaseConfiguration.Build()
    .UsingConnectionStringName("sqlite")
    .UsingDefaultMapper<ConventionMapper>(m =>
    {
        m.FromDbConverter = (targetProperty, sourceType) =>
        {
            if (targetProperty == null)
                return null;

            var t = targetProperty.PropertyType;

                    if (t.BaseType == null || ! t.BaseType.IsGenericType) 
                        return null;

                    if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
                        return null;

                    return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetFromDbConverter(targetProperty, sourceType);
        };
        m.ToDbConverter = sourceProperty =>
        {
            if (sourceProperty == null)
                return null;

            var t = sourceProperty.PropertyType;

                    if (t.BaseType == null || !t.BaseType.IsGenericType)
                        return null;

                    if (t.BaseType.GetGenericTypeDefinition() != typeof(Headspring.Enumeration<>))
                        return null;

                    return ((IMapper)Activator.CreateInstance(typeof(EnumClassMapper<>).MakeGenericType(t))).GetToDbConverter(sourceProperty);
        };
    });

var db = builder.Create();
person Plebsori    schedule 01.02.2016
comment
У вас есть ссылка на PetType в вашем классе картографа - person Aheho; 01.02.2016
comment
Здесь: return (x) => PetType.FromValue((int) x); - person Aheho; 01.02.2016
comment
и typeof(PetType).GetTypes() не компилируется. - person Aheho; 01.02.2016
comment
Параметр типа в методе Mapper.Register(type, IMapper) — это тип класса POCO, а не тип содержащегося в нем столбца. Существует перегрузка, которая будет обрабатывать любые классы poco, найденные в сборке, но ее многократный вызов просто перезаписывает предыдущий зарегистрированный преобразователь. Я думаю, что код ForEach должен быть перемещен внутри самого класса картографа. - person Aheho; 01.02.2016
comment
Обновлено для использования гибкой конфигурации. Если это работает, возможно, вам придется немного оптимизировать его. Я сделал акцент на работе над эффективностью. - person Plebsori; 02.02.2016
comment
Я внес некоторые изменения в ваш пост, которые в конечном итоге решили мою проблему. Голосование и пометка как правильный ответ. - person Aheho; 05.02.2016