如何从基本类型投射到仅在运行时已知的类型?

5
我们正在使用Entity Framework Core,并且有一组实体都共享同一个基类。
public class EntityBase { ... }

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

我们使用这个功能可以在编译时不需要知道是 FirstEntityChild 还是 SecondEntityChild,从而查询其中任一实体子类。

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

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

我们将其用于如下场景,假设 sourceType 可能是 FirstEntityChild 或者 SecondEntityChildtargetType 可能是 FirstEntityChildViewModel 或者 SecondEntityChildViewModel,而且两者仅在运行时才知道:

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

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

我们所面临的问题出现在我们的 ProjectTo 扩展内。当我在那一行调试并停下时,sourceTypeInstancedestinationTypeInstance 在调试器中会分别显示为 FirstEntityChildFirstEntityChildViewModel。但是当该行执行时,AutoMapper 试图将映射从 EntityBaseobject 而我们没有这个映射配置文件(而且我认为这甚至不可能):

InvalidOperationException: Missing map from EntityBase to System.Object. Create using Mapper.CreateMap.

有没有可能指示 AutoMapper 应该使用实际的派生类型?或者我们现在只是碰到了泛型的限制?


你是否有从FirstEntityChildFirstEntityChildViewModel的映射?还是只有从EntityBase到其他东西的映射? - Vidmantas Blazevicius
我们确实有从FirstEntityChildFirstEntityChildViewModel和从SecondEntityChildSecondEntityChildViewModel的映射。没有从EntityBase到其他任何内容的映射。感谢您指出这一点,我已经编辑了问题以反映这一点。 - Jimmy
我的意思是这里一定有更多的东西,为什么你要通过Activator创建实例,然后立即进行映射呢?即使你这样做了,将default(TSource)映射到default(TDestination)是否更好?既然你打算从EF中获取源,那么为什么还需要sourceInstance呢? - Vidmantas Blazevicius
我敢打赌,你的 mapper.ConfigurationProvider 可能没有保存你的映射配置。 - Vidmantas Blazevicius
让我们在聊天中继续这个讨论 - Jimmy
显示剩余3条评论
1个回答

0
你可以使用反射来创建一个通用方法,将 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[] { } });
}

我完全不明白这有什么帮助。如果在运行时还不知道TSource和TDestination类型,你怎么能调用扩展方法呢? - DiscipleMichael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接