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

本文关键字:模型 类型 视图 继承 数据 对象 映射 | 更新日期: 2023-09-27 18:35:39

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

namespace MyEntities
{
    Person
    Employee : Person
    Customer : Person
    WebCustomer : Customer
}

而且我在另一个命名空间中也有相同的类名,用作视图模型的基础:

namespace ViewModels
{
    Person
    Employee : Person
    Customer : Person
    WebCustomer : Customer
}

然后,我可以做一些有趣的事情,例如使用泛型方法创建已知类型的视图模型:

public T Make<T>(Entities.Person entity) where T : ViewModels.Person, new()
{
    T model = (T)new T().InjectFrom(entity);
    return model;
}

但是我不知道该怎么做是在我事先不知道类型的情况下制作正确类型的模型 - 我希望能够做这样的事情:

ViewModels.Person person = GetPersonById(24);

并让该人员对象属于底层类型之一 - 例如 WebCustomer。

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

如果我允许实体冒泡到网站,我可以使用编辑器和显示模板,但如果我想在此过程中具有映射逻辑,我似乎无法正确获取类型信息。

我想需要的是一种方法,例如:

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

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

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

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

public static PersonViewModelFactory
{
    public static TViewModel CreateFrom<TViewModel>(TEntity entity)
        where TViewModel : PersonViewModel, new()
    {
        return new TViewModel { // map properties from Person }
    }
}

然后,要使用它,您只需执行以下操作:

var viewModel = PersonViewModelFactory.CreateFrom<CustomerViewModel>(person);

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

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

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

MyEntities entity
ViewModels.Person person = Activator.CreateInstance(
    Type.GetType("ViewModels" + "." + entity.GetType().Name));

然后,您可以创建动态映射或使用 EmitMapper 等库在对象之间进行映射。

请记住,这些类型的方法总是会有一些性能损失。 也就是说,如果利润足够小,您对此感到满意,它将为您提供所需的动态方法。