How to ProjectTo from a base type to a type only known at runtime?

automapper c# entity-framework-core

Question

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?

1
3
8/15/2019 3:53:59 PM

Accepted Answer

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[] { } });
}
0
8/19/2019 2:49:21 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow