Search code examples
c#enumerationpetapoco

Designing a custom PetaPoco mapper that supports HeadSprings Enumeration Class


I'm attempting to create a mapper so PetaPoco can hydrate and persist POCOs with Enumeration class properties. See more about Enumeration classes here or here.

For instance, Take this class.

    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) { }
     }

Which can be used like so:

var MyPet = PetType.Dog;

Here is the Poco I want to hydrate/persist with the database:

    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; }
    }

I have designed a custom mapper that will work with 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);
       }
   }

However suppose I create another Enumeration subclass for disposition.

    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) { }
     }

I don't want to have to update my mapper every time I create a new subclass of the Enumeration class. I prefer that the mapping code could recognize that the property type is a descendent of the Enumeration class, and map accordingly. I assume the answer is to make use of reflection, but I don't know how to proceed.


Solution

  • What about

    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();