如何将 ProjectTo 从基本类型转换为仅在运行时已知的类型?

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

我们正在使用 Entity Framework 核心,其中包含一组共享基础 class 的实体。

public class EntityBase { ... }

public class FirstEntityChild : EntityBase { ... }
public class SecondEntityChild : EntityBase { ... }

我们使用它来查询 FirstEntityChildSecondEntityChild 而无需在编译时知道是哪一个。

为了向我们的视图提供数据,我们有视图模型。

public class FirstEntityChildViewModel { ... }
public class SecondEntityChildViewModel { ... }

我们希望使用 AutoMapper 的 ProjectTo 将查询结果从我们的数据库实体映射到视图模型。我们设置了从 FirstEntityChildFirstEntityChildViewModel 和从 SecondEntityChildSecondEntityChildViewModel 的映射配置文件。我们没有来自 EntityBase.

的地图

我们目前正在尝试使用扩展方法通过类型推断来完成此任务:

public static IQueryable<TDestination> ProjectTo<TSource, TDestination>(
    this IQueryable<TSource> query,
    TSource sourceTypeInstance,
    TDestination destinationTypeInstance,
    IConfigurationProvider configuration)
{
    return query.ProjectTo<TDestination>(configuration);
}

我们按如下方式使用它,假设 sourceTypeFirstEntityChildSecondEntityChild,并且 targetType 是 FirstEntityChildViewModelSecondEntityChildViewModel ,两者都只在运行时才知道:

var sourceInstance = Activator.CreateInstance(sourceType);
var targetInstance = Activator.CreateInstance(targetType);

var results = await query
    .ProjectTo(sourceInstance, targetInstance, mapper.ConfigurationProvider)
    .ToListAsync();

我们面临的问题发生在我们的 ProjectTo 扩展中。当我调试并停在该行时,sourceTypeInstancedestinationTypeInstance 在调试器中分别显示为 FirstEntityChildFirstEntityChildViewModel。但是当该行执行时,AutoMapper 尝试从 EntityBase 映射到 object 我们没有对应的映射配置文件(我也没有认为甚至是可能的):

InvalidOperationException:缺少从 EntityBase 到 System.Object 的映射。使用 Mapper.CreateMap.

创建

是否可以指出 AutoMapper 应该使用的实际派生类型?还是我们现在只是遇到了泛型的局限性?

您可以使用反射从 ProjectTo 和您的目标类型创建一个通用方法,然后调用该方法来获取 ProjectTo 的结果。例如:

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[] { } });
}