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

时间:2016-02-22 16:54:49

标签: c# asp.net-mvc entity-framework

假设我有一些使用Entity Framework创建的类,它们使用继承。例如:

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

2 个答案:

答案 0 :(得分:2)

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

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。毕竟,为什么重新发明轮子?

答案 1 :(得分:0)

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

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

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

请记住,这些类型的方法总会有一些性能损失。也就是说,如果它足够小,你会对它感到满意,那么它将为你提供你想要的动态方法。