I'm using Table Per Hierarchy database inheritance where columns for all derived types are in a single table. Each derived table is identified using a string Discriminator field that holds the name of the derived class:
---------------------
| tanimal |
---------------------
| animalid |
| discriminator |
| furcolour |
| feathercolour |
---------------------
public abstract class Animal
{
public int AnimalId { get; set; }
public string Discriminator { get { return GetType().Name; } }
}
public class Bird : Animal
{
public string FeatherColour { get; set; }
}
public class Dog : Animal
{
public string FurColour { get; set; }
}
As expected, when retrieving this via Dapper's query method I receive Instances of abstract classes cannot be created
. I would hope that this would return a list of Animal with their values being the respective derived types.
var animals = Connection.Query<Animal>("SELECT * FROM tanimal")
My attempts to add support for this have been unsuccessful. Before SqlMapper.cs::GetTypeDeserializer() is called if the type being passed in is an abstract class then I replace the type with the one returned in the following method:
static Type GetDerivedType(Type abstractType, IDataReader reader)
{
var discriminator = abstractType.GetProperty("Discriminator");
if (discriminator == null)
throw new InvalidOperationException("Cannot create instance of abstract class " + abstractType.FullName + ". To allow dapper to map to a derived type, add a Discriminator field that stores the name of the derived type");
return Type.GetType((string)reader["Discriminator"]);
}
However it looks like at this point the reader hasn't been opened so it fails with Invalid attempt to read when no data is present
.
Is this the correct approach to take? Has there been any effort to support this elsewhere?