从这个问题来看,让控制器创建一个更准确地反映视图试图显示的模型的ViewModel似乎是有意义的,但我对一些约定很好奇。

基本上,我有以下几个问题:

我通常喜欢有一个类/文件。如果创建ViewModel只是为了将数据从控制器传递给视图,那么这对ViewModel有意义吗? 如果ViewModel属于它自己的文件,并且您正在使用目录/项目结构来保持内容分离,那么ViewModel文件属于哪里?在Controllers目录下?

现在基本上就是这样了。我可能会有更多的问题,但这已经困扰了我一个小时左右,我似乎可以在其他地方找到一致的指导。

编辑: 查看CodePlex上的示例NerdDinner应用程序,它看起来像视图模型是控制器的一部分,但它仍然让我不舒服,因为它们不在自己的文件中。


当前回答

下面是我的最佳实践代码片段:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

其他回答

There are no good place to keep your models in. You can keep them in separate assembly if the project is big and there are a lot of ViewModels (Data Transfer Objects). Also you can keep them in separate folder of the site project. For example, in Oxite they are placed in Oxite project which contains a lot of various classes too. Controllers in Oxite are moved to separate project and views are in separate project too. In CodeCampServer ViewModels are named *Form and they are placed in UI project in Models folder. In MvcPress project they are placed in Data project, which also contains all code to work with database and a bit more (but I didn't recommend this approach, it's just for a sample) So you can see there are many point of view. I usually keep my ViewModels (DTO objects) in the site project. But when I have more than 10 models I prefer to move them to separate assembly. Usually in this case I'm moving controllers to separate assembly too. Another question is how to easily map all data from model to your ViewModel. I suggest to have a look at AutoMapper library. I like it very much, it does all dirty work for me. And I also I suggest to look at SharpArchitecture project. It provides very good architecture for projects and it contains a lot of cool frameworks and guidances and great community.

我将我的应用程序类保存在一个名为“Core”的子文件夹中(或一个单独的类库),并使用与KIGG示例应用程序相同的方法,但做了一些轻微的更改,以使我的应用程序更加DRY。

我在/Core/ViewData/中创建了一个BaseViewData类,其中存储了常见的站点属性。

在此之后,我还在同一个文件夹中创建了所有的视图ViewData类,然后从BaseViewData派生并具有视图特定的属性。

然后我创建一个ApplicationController,我所有的控制器都是从它派生出来的。ApplicationController有一个通用的GetViewData方法,如下:

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

最后,在我的控制器动作中,我做以下工作来构建我的ViewData模型

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

我认为这工作得很好,它保持你的视图整洁,你的控制器瘦。

在我们的例子中,我们将模型和控制器放在一个独立于视图的项目中。

根据经验,我们已经尝试移动和避免大多数ViewData[”…]的东西,因此我们避免了类型转换和魔术字符串,这是一件好事。

ViewModel还包含一些常见的属性,如列表的分页信息或用于绘制面包屑和标题的页眉信息。在我看来,此时基类包含了太多的信息,我们可以将其分为三部分,基本视图模型中99%的页面的最基本和必要的信息,然后是列表模型和表单模型,其中包含了该场景的特定数据并继承自基类。

最后,我们为每个实体实现一个视图模型来处理特定的信息。

ViewModel类用于将由类实例表示的多个数据封装到一个易于管理的对象中,您可以将该对象传递给View。

将ViewModel类放在自己的文件中,放在自己的目录中是有意义的。在我的项目中,我有一个名为ViewModels的Models文件夹子文件夹。这就是我的ViewModels(例如ProductViewModel.cs)所在的位置。

创建一个视图模型基类,它具有通常需要的属性,如操作结果和上下文数据,您还可以放置当前用户数据和角色

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

在基控制器类中,有一个类似PopulateViewModelBase()的方法,该方法将填充上下文数据和用户角色。 HasError和ErrorMessage,如果从service/db中提取数据时出现异常,请设置这些属性。在视图上绑定这些属性以显示错误。 用户角色可用于根据角色在视图中显示隐藏部分。

要在不同的get操作中填充视图模型,可以通过具有具有抽象方法FillModel的基本控制器使之一致

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

在控制器

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}