We are using Entity Framework Core, with a set of Entities that all share a base class.
public class EntityBase { ... }
public class FirstEntityChild : EntityBase { ... }
public class SecondEntityChild : EntityBase { ... }
We use this so we can query for either a FirstEntityChild
or SecondEntityChild
without needing to know which one at compile time.
To provide the data to our view, we have view models.
public class FirstEntityChildViewModel { ... }
public class SecondEntityChildViewModel { ... }
We're hoping to use AutoMapper's ProjectTo
to map the query results from our database entity to a view model. We have mapping profiles set up from FirstEntityChild
to FirstEntityChildViewModel
and from SecondEntityChild
to SecondEntityChildViewModel
. We have no maps from EntityBase
.
We're currently attempting to accomplish this through type inference with an extension method:
public static IQueryable<TDestination> ProjectTo<TSource, TDestination>(
this IQueryable<TSource> query,
TSource sourceTypeInstance,
TDestination destinationTypeInstance,
IConfigurationProvider configuration)
{
return query.ProjectTo<TDestination>(configuration);
}
And we're using that as follows, given that sourceType
is either FirstEntityChild
or SecondEntityChild
, and targetType is either FirstEntityChildViewModel
or SecondEntityChildViewModel
, and both are known only at runtime:
var sourceInstance = Activator.CreateInstance(sourceType);
var targetInstance = Activator.CreateInstance(targetType);
var results = await query
.ProjectTo(sourceInstance, targetInstance, mapper.ConfigurationProvider)
.ToListAsync();
The issue we're facing happens inside our ProjectTo
extension. When I debug and stop on that line, sourceTypeInstance
and destinationTypeInstance
appear in the debugger to be FirstEntityChild
and FirstEntityChildViewModel
respectively. But when the line executes, AutoMapper attempts to map from EntityBase
to object
, which we don't have a mapping profile for (and which I don't think would even be possible):
InvalidOperationException: Missing map from EntityBase to System.Object. Create using Mapper.CreateMap.
Is it possible to indicate the actual, derived type AutoMapper should be using? Or are we just bumping up against the limitations of generics at this point?
You could use reflection to create a generic method out of ProjectTo and your destination type and then invoke that method to get the results of ProjectTo. For instance:
public static IQueryable<TDestination> ProjectTo<TSource, TDestination>(
this IQueryable<TSource> query,
TSource sourceTypeInstance,
TDestination destinationTypeInstance,
IConfigurationProvider configuration)
{
var projectToMethod = typeof(AutoMapper.QueryableExtensions.Extensions)
.GetMethod("ProjectTo", new Type[]
{
typeof(IQueryable),
typeof(IConfigurationProvider),
typeof(IDictionary<string, object>),
typeof(string[])
})
.MakeGenericMethod(typeof(TDestination));
return projectToMethod.Invoke(query, new object[] { query, configuration, new Dictionary<string, object>(), new string[] { } });
}