ASP.NET MVC模型与其他辅助实体的viewmodel映射

时间:2011-11-30 04:45:42

标签: c# asp.net asp.net-mvc automapper ravendb

我正在使用ASP.NET MVC 3,Raven DB作为后备数据存储。我有一组模型,我有兴趣转换成ViewModels。为了实现这一点,我正在利用AutoMapper来处理将每个属性映射到ViewModel中相应属性的肮脏工作。让我们说我有一个这样的模型:

public class FooModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int AlphaId { get; set; }
    public int BetaId { get; set; }
}

然后让我说我想把它转换成像这样的ViewModel:

public class FooViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int AlphaId { get; set; }
    public Alpha Alpha { get; set; }
    public int BetaId { get; set; }
    public Beta Beta { get; set; }
}

然后在app启动时将我的地图设置为这样:

Mapper.CreateMap<Foo, FooViewModel>();

然后,在控制器中我按原样执行地图:

public ActionResult FooDetails(string id)
{
    using(var session = this.documentStore.OpenSession())
    {
        var fooInstance = session.Load<Foo>(id);
        var fooViewModel = Mapper.Map<FooViewModel>(fooInstance);
        return this.View(fooViewModel);
    }
}

问题在于,如上所示,从存储库中出来的实体有2个属性,这些属性是Alpha和Beta类型的其他对象的键。我对基于AlphaId和BetaId键的Alpha和Beta保湿感兴趣。

起初我以为我会利用AutoMapper的自定义转换功能,但我认为这不会起作用,因为我们需要将数据会话注入映射(调用数据存储来检索Alpha或Beta对象)。

另一种选择是在控制器动作中完成所有工作,但很快就会变得笨拙(不是在给定的特定示例中,但这只是一个例子来说明这一点)。

Alpha和Beta的水合应该在哪里发生,这里有什么好的模式?

4 个答案:

答案 0 :(得分:2)

我同意那里的人,但是如果你不能扩展你的模型,你总是可以用AfterMap定义映射:

Mapper.CreateMap<Foo, FooViewModel>()
.AfterMap(MapAlphaBetaFromFooToFooViewModel);


public void MapAlphaBetaFromFooToFooViewModel(Foo foo, FooViewModel fooViewModel)
{
// Here the code for loading and mapping you objects
}

这样,当您进行映射时,Automapper将在基本映射完成后自动运行该方法。

答案 1 :(得分:1)

它可能并不理想,但你不能只为这两个引用创建地图并在事后映射它们。您仍然必须单独调用它们,但是您要让AutoMapper执行繁重的映射,而不是使用您自己的映射填充控制器。

public ActionResult FooDetails(string id)
{
    using(var session = this.documentStore.OpenSession())
    {
        var foo = session.Load<Foo>(id).Include(....);
        var alpha = session.Load<Alpha>(foo.AlphaId);
        var beta = session.Load<Beta>(foo.BetaId);

        // null checks

        var fooViewModel = Mapper.Map<FooViewModel>(foo);
        fooViewModel.Alpha = Mapper.Map<AlphaViewModel>(alpha);
        fooViewModel.Beta = Mapper.Map<BetaViewModel>(beta);

        return View(fooViewModel);
    }
}  

我还没有使用过AutoMapper,但也许你可以通过Map函数传递子对象(在CreateMap上提供ForMember链)?也许亚力山大的AfterMap建议可能有用,但是我没有看到你如何在没有在控制器动作中创建地图的情况下给“被引用的”孩子保湿(但是你可以直接使用ForMember和Raven会话并且只有一个Mapper.Map)。

添加 - AutoMapper文档为嵌套/子地图提供了类似的方法 - https://github.com/AutoMapper/AutoMapper/wiki/Nested-mappings。虽然它更类似于对象引用,而不是对象id引用情况。

我还不能对其他答案发表评论,但关于Pavel的答案 - 你并没有真正以这种方式使用Raven,没有o / r映射(除了内部JSON - &gt;对象)或者数据访问层,您只需直接使用Raven会话(可能通过服务,但肯定不需要存储库或类似)。关于引用,字节是正确的,并且当他评论其中一个答案时,最好的方法是存储id引用并使用Raven包含 - 它是相同的结果并且仍然只使用一个请求。

答案 2 :(得分:0)

您可以做的一个优化是从RavenDB一次加载相关文档,而不是单独调用以加载每个相关文档。请参阅RavenDB文档here

我无法想到除了在控制器本身中执行此操作之外的任何其他方式来映射这些实体,如您所示。

答案 3 :(得分:0)

您应该首先关心您的架构。从数据库加载数据是数据访问层的责任,它不应该知道您的UI是如何组织的。

我认为,最好的方法是以下责任分配:

  1. 数据访问层具有基于POCO的模型,其中实体彼此之间具有引用而不是标识符。此模型暴露给更高层(例如视图模型)。
  2. DAL有一个方法可以加载预期操作所需的所有数据。在你的情况下,它应该加载Foo,Alpha,Beta实体。由于这发生在一个DAL方法中,它可能在单个数据库查询中执行此操作。虽然主要技巧是其他层不关心它是如何工作的。
  3. 在控制器中,然后使用AutoMapper将FooModel对象展平为FooViewModel,这是Automapper功能之一。
  4. 关键点是:

    1. 只有DAL知道它如何加载数据。如有必要,可以根据需要进行修改,而无需修改与AutoMapper相关的代码。
    2. AutoMapper仅将数据从一个结构映射到另一个结构,不会影响数据加载策略。
    3. 那就是说,我只会修改你的FooModel:

      public class FooModel
      {
        public int Id { get; set; }
        public string Name { get; set; }
        public Alpha Alpha { get; set; }
        public Beta Beta { get; set; }
      }
      

      以及从DB加载它的代码。

      但是,您可能需要原始的类似FooModel的结构,例如,定义与该简单结构的对象关系映射。但它仍然完全取决于DAL。