C# 如何将继承的数据对象映射到相应的视图模型而不丢失其类型?

C# 如何将继承的数据对象映射到相应的视图模型而不丢失其类型?,c#,asp.net-mvc,entity-framework,C#,Asp.net Mvc,Entity Framework,假设我有一些使用实体框架创建的类,它们使用继承。例如: namespace MyEntities { Person Employee : Person Customer : Person WebCustomer : Customer } public static PersonViewModelFactory { public static TViewModel CreateFrom<TViewModel>(TEntity entity)

假设我有一些使用实体框架创建的类,它们使用继承。例如:

namespace MyEntities
{
    Person
    Employee : Person
    Customer : Person
    WebCustomer : Customer
}
public static PersonViewModelFactory
{
    public static TViewModel CreateFrom<TViewModel>(TEntity entity)
        where TViewModel : PersonViewModel, new()
    {
        return new TViewModel { // map properties from Person }
    }
}
我在另一个命名空间中也有相同的类名,用作视图模型的基础:

namespace ViewModels
{
    Person
    Employee : Person
    Customer : Person
    WebCustomer : Customer
}
然后我可以做一些有趣的事情,比如使用通用方法创建已知类型的viewmodel:

public T Make<T>(Entities.Person entity) where T : ViewModels.Person, new()
{
    T model = (T)new T().InjectFrom(entity);
    return model;
}
让person对象属于底层类型之一,比如WebCustomer

这将允许我使用MVC.net中的EditorFor和DisplayFor模板为正确类型的人显示正确的控件,即使只是按Id抓取一个控件

如果我允许实体冒泡到网站上,我可以使用编辑器和显示模板,但是如果我想有映射逻辑,我似乎无法获得正确的类型信息

我想我们需要的是这样一种方法:

ViewModels.Person person Get(MyEntities entity)
{
// ??
}

其中返回的person对象按名称映射并强制转换为正确的基础类型。有没有办法实现这种行为?还是有更好的方法来处理通过模型映射器传递继承的结构,而我应该这样做

根据我们在评论中的讨论,是的,你是对的。如果您有确切的名称,您可以替换名称空间信息,并使用
Activator
创建实例,前提是您的名称空间始终一致

MyEntities entity
ViewModels.Person person = Activator.CreateInstance(
    Type.GetType("ViewModels" + "." + entity.GetType().Name));
然后,您可以创建动态映射,或者使用EmitMapper之类的库在对象之间进行映射


请记住,对于这些类型的方法,总会有一些性能损失。这就是说,如果它是一个足够小的利润,您可以放心地使用它,它将为您提供您正在寻找的动态方法。

您只需要一个通用工厂。以以下代码为例:

namespace MyEntities
{
    Person
    Employee : Person
    Customer : Person
    WebCustomer : Customer
}
public static PersonViewModelFactory
{
    public static TViewModel CreateFrom<TViewModel>(TEntity entity)
        where TViewModel : PersonViewModel, new()
    {
        return new TViewModel { // map properties from Person }
    }
}
公共静态PersonViewModelFactory
{
公共静态TViewModel CreateFrom(TEntity实体)
其中TViewModel:PersonViewModel,new()
{
返回新的TViewModel{//map properties from Person}
}
}
然后,要使用它,您只需执行以下操作:

var viewModel = PersonViewModelFactory.CreateFrom<CustomerViewModel>(person);
var viewModel=PersonViewModelFactory.CreateFrom(person);
然后,
viewModel
将被键入为
CustomerViewModel
。但是,重要的是要认识到,您在这里使用的是最小公分母,因此,即使您返回的是
CustomerServiceWModel
,您可以使用的唯一属性是
Person
上存在的属性,而不是
Customer
。访问
Customer
属性的唯一方法是重载方法以将
Customer
的实例作为参数,或者将实体参数设置为泛型。但是,如果两者都是泛型的,那么它们都需要实现一个通用接口。这样,您可以将类型参数约束到接口,并访问接口定义的属性


长话短说,创建一个真正的泛型类型映射器是一件痛苦的事情。您要么接受我提到的方法的局限性,要么就使用现有的库来实现这类功能,比如AutoMapper。毕竟,为什么要重新发明轮子?

您希望如何仅通过Id检索任何潜在的特定类型模型?您需要按类型约束控制器,或者必须包含其他元数据,以了解id应与哪种类型相关。@DavidL-我希望使用TPT继承结构让实体框架按id进行计算-如果我在web UI中使用实体层,这将非常有效。我可以传回一个Id,让EF传回一个正确类型的对象,MVC中的DisplayFor可以正确地提供相应的视图——我唯一不喜欢的是它需要Web UI引用实体框架类型,但您正在破坏TPT继承结构。因此,这个论点不再成立,因此我认为您需要额外的元数据。一旦打破继承链,Id就不再足够了。作为记录,我同意。您不应该向web层公开EF类型。@DavidL-我明白您的意思。我希望找到一种方法,使用EF提供的类型作为创建视图模型所需的元数据,从而避免向下传递该元数据。如果EF可以使用TPT创建正确类的实体对象,并且我知道我的视图模型类具有完全相同的名称,那么理论上我就拥有了我需要的所有信息,对吗?(虽然我知道这并不意味着在实践中是可能的。)