我应该在ViewModel和DomainModel之间有一个图层吗?

时间:2014-08-19 19:39:09

标签: asp.net-mvc viewmodel domain-model

我有我的域模型(用于绑定EF并用于映射到数据库):

public class Category : BaseEntity
{
    public string Name { get; set; }
    public Category ParentCategory { get; set; }
    //Code removed for brevity
}

然后我有一个共享控制器在我的视图中显示一个类别菜单:

@Html.Action("GetCategoryMenu", "Shared")

控制器

[OutputCache(Duration = int.MaxValue, VaryByParam = "none")]
public ActionResult GetCategoryMenu()
{
    var viewModel = new CategoryMenuViewModel
    {
        Categories = _categoryService.Get().Where(c => c.ParentCategory != null && c.ParentCategory.Id == WebsiteContext.CurrentWebsite.CategoryId)
    };

    return PartialView("_CategoryMenu", viewModel);
}

ViewModel:

public class CategoryMenuViewModel
{
    public IEnumerable<Category> Categories { get; set; }
}

我现在的问题是,如果我在我的视图中实现了category.GetUrl()之类的内容,那会是什么?域模型是否适合这样的事情?

GetUrl()会根据某些条件运行一些逻辑并返回一个Url。我看到这是从视图本身使用嵌入到超链接。它将按类别运行,基本上只是获取一个Url,以防我想改变以后生成它们的方式。

基本上,我试图在部分视图中实现这一点,以呈现类别菜单:

@foreach (Category category in Model.Categories)
{
    <li class="list-group-item clearfix">
        <a href="@category.GetUrl()"><i class="fa fa-angle-right"></i> @category.Name</a>
    </li>
}

我现在只是在寻找最佳实践建议,因为我想确保我正确理解MVC模式,因为我对将GetUrl()方法放在我的域模型上有一种奇怪的感觉,但我不知道为什么。

2 个答案:

答案 0 :(得分:2)

  

我现在的问题是,如果我在我的视图中实现了类似category.GetUrl()的内容,那会是什么?域模型是否适合这样的事情?

没有!如果明天你的公司决定写一个IPad应用程序怎么办?您的域模型仍然有效 - 业务逻辑和权限检查以及所有这些仍然适用 - 但您不再使用URL。显然,URL生成逻辑不属于此。

在ASP.NET MVC中,生成URL的责任通常在于UrlHelper类的方法。通常,直接使用这些方法很容易:

<a href="@Url.Action("Category", new{category.Id})"><i class="fa fa-angle-right"></i> @category.Name</a>

但是如果您需要重复使用代码,我建议您在UrlHelper上以扩展方法的形式执行此操作:

<a href="@Url.Category(category)"><i class="fa fa-angle-right"></i> @category.Name</a>

答案 1 :(得分:1)

基本思想是以这样一种方式创建应用程序,即可以替换彼此之上的层而不触及底层。一个典型的例子就是three-tier architecture。这意味着您的GUI只访问逻辑层(业务模型),而您的逻辑层只访问数据库层(实体框架)。由于实体框架的设计方式,最后一部分有点可疑:当您将业务逻辑放入实体类本身时,EF已经将逻辑层与数据层混合在一起。

但是,对于上面两层,你应该尊重这种分离。执行GUI任务的方法(例如GetUrl)不应该是域类的一部分。它可能是例如在将来你需要更改GUI,你的URL现在有不同的布局。然后,您需要更改域模型,而不是它应该如何。

正确的方法是创建一个额外的视图模型,然后将业务对象所需的属性映射到视图模型。但是,您真的想避免这种情况,您可能会考虑在GUI项目中为业务类定义扩展方法。

旁注:我完全按照您的建议行事,即使用通过Entity Framework获得的业务对象作为视图模型。请注意这带来的限制,特别是在将更改保存回EF时。您通常仍需要映射发布给您的对象,即使它们是实体,因为它们由MVC模型绑定器实例化,并且不通过EF上下文检索,即它们不能保存为现有对象的更新版本。解决这个问题的一种方法可能是创建一个自定义模型绑定器,但我认为通过创建视图模型和映射会更好。