ViewModel 最佳实践

发布于 2024-07-15 12:31:49 字数 799 浏览 8 评论 0原文

这个问题来看,让控制器创建一个ViewModel 更准确地反映视图尝试显示的模型。 不过,我对一些约定很好奇。

基本上,我有以下问题:

  1. 我通常喜欢有一个类/文件。 如果创建 ViewModel 只是为了将数据从控制器传递到视图,那么这种(偏好)对 ViewModel 有意义吗?
  2. 如果 ViewModel 确实属于其自己的文件,并且您使用目录/项目结构来将各个内容分开,那么 ViewModel 文件属于哪里? 在 Controllers 目录中?

现在基本上就是这样。 我可能还有几个问题要问,但这在过去一个小时左右一直困扰着我; 我似乎在其他地方找不到一致的指导。

编辑: 查看 CodePlex 上的示例 NerdDinner 应用,看起来 ViewModel 是 控制器,但它们不在自己的文件中仍然让我感到不舒服。

From this question, it looks like it makes sense to have a controller create a ViewModel that more accurately reflects the model that the view is trying to display. However, I'm curious about some of the conventions.

Basically, I had the following questions:

  1. I, normally, like to have one class/file. Does this (preference) make sense with a ViewModel if it is only being created to hand off data, from a controller to a view?
  2. If a ViewModel does belong in its own file, and you're using a directory/project structure to keep things separate, where does the ViewModel file belong? In the Controllers directory?

That's, basically, it for now. I might have a few more questions coming up, but this has been bothering me for the last hour, or so; and I can't seem to find consistent guidance, elsewhere.

EDIT:
Looking at the sample NerdDinner app on CodePlex, it looks like the ViewModels are part of the Controllers, but it still makes me uncomfortable that they aren't in their own files.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

寂寞美少年 2024-07-22 12:31:50

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

我在 /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();
    }

我认为这非常有效,它使您的视图保持整洁,控制器保持精简。

I keep my application classes in a sub folder called "Core" (or a seperate class library) and use the same methods as the KIGG sample application but with some slight changes to make my applications more DRY.

I create a BaseViewData class in /Core/ViewData/ where I store common site wide properties.

After this I also create all of my view ViewData classes in the same folder which then derive from BaseViewData and have view specific properties.

Then I create an ApplicationController that all of my controllers derive from. The ApplicationController has a generic GetViewData Method as follows:

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

Finally, in my Controller action i do the following to build my ViewData Model

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

I think this works really well and it keeps your views tidy and your controllers skinny.

歌入人心 2024-07-22 12:31:50

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

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

A ViewModel class is there to encapsulate multiple pieces of data represented by instances of classes into one easy to manage object that you can pass to your View.

It would make sense to have your ViewModel classes in their own files, in the own directory. In my projects I have a sub-folder of the Models folder called ViewModels. That's where my ViewModels (e.g. ProductViewModel.cs) live.

落在眉间の轻吻 2024-07-22 12:31:50

没有好地方来保存模型。如果项目很大并且有很多 ViewModel(数据传输对象),则可以将它们保存在单独的程序集中。 您也可以将它们保存在站点项目的单独文件夹中。 例如,在 Oxite 中,它们被放置在 Oxite 项目中,该项目也包含许多不同的类。 Oxite 中的控制器已移至单独的项目,视图也位于单独的项目中。
CodeCampServer 中,ViewModel 被命名为 *Form,并放置在 Models 文件夹中的 UI 项目中。
MvcPress 项目中,它们被放置在 Data 项目中,该项目还包含使用数据库的所有代码以及更多内容(但我不推荐这种方法,这只是一个示例)
所以你可以看到有很多观点。 我通常将 ViewModel(DTO 对象)保留在站点项目中。 但当我有超过 10 个模型时,我更喜欢将它们分开组装。 通常在这种情况下,我也会将控制器移动到单独的组件中。
另一个问题是如何轻松地将所有数据从模型映射到 ViewModel。 我建议查看 AutoMapper 库。 我非常喜欢它,它为我做了所有脏活。
我还建议查看 SharpArchitecture 项目。 它为项目提供了非常好的架构,并且包含许多很酷的框架和指南以及很棒的社区。

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.

诗酒趁年少 2024-07-22 12:31:50

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

    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");
        }
}

here's a code snippet from my best practices:

    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");
        }
}
那一片橙海, 2024-07-22 12:31:50

我们将所有 ViewModel 放入 Models 文件夹中(所有业务逻辑都位于单独的 ServiceLayer 项目中)

We throw all of our ViewModels in the Models folder (all of our business logic is in a separate ServiceLayer project)

你是我的挚爱i 2024-07-22 12:31:50

就我个人而言,我建议如果 ViewModel 不那么简单,那么就使用一个单独的类。

如果您有多个视图模型,那么我建议至少将其分区到一个目录中。 如果稍后共享视图模型,则目录中隐含的名称空间可以更轻松地移动到新程序集。

Personally I'd suggest if the ViewModel is anything but trivial then use a separate class.

If you have more than one view model then I suggest it make sense to partition it in at least a directory. if the view model is later shared then the name space implied in the directory makes it easier to move to a new assembly.

眼前雾蒙蒙 2024-07-22 12:31:50

在我们的例子中,我们在与视图分开的项目中拥有模型和控制器。

根据经验,我们尝试将大多数 ViewData["..."] 内容移动并避免到 ViewModel,从而避免强制转换和魔术字符串,这是一件好事。

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

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

In our case we have the Models along with the Controllers in a project separate from the Views.

As a rule of thumb, we've tried to move and avoid most of the ViewData["..."] stuff to the ViewModel thus we avoid castings and magic strings, which is a good thing.

The ViewModel as well holds some common properties like pagination information for lists or header information of the page to draw breadcrumbs and titles. At this moment the base class holds too much information in my opinion and we may divide it in three pieces, the most basic and necessary information for 99% of the pages on a base view model, and then a model for the lists and a model for the forms that hold specific data for that scenarios and inherit from the base one.

Finally, we implement a view model for each entity to deal with the specific information.

热风软妹 2024-07-22 12:31:50

控制器中的代码:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

视图模型中的代码:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

项目:

  • DevJet.Web ( ASP.NET MVC web
    项目)

  • DevJet.Web.App.Dictionary(
    单独的类库项目)

    在这个项目中,我制作了一些文件夹,例如:
    达尔,
    黑线,
    博,
    VM(视图模型文件夹)

code in the controller:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

code in view model:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

projects:

  • DevJet.Web ( the ASP.NET MVC web
    project)

  • DevJet.Web.App.Dictionary ( a
    seperate Class Library project)

    in this project, i made some folders like:
    DAL,
    BLL,
    BO,
    VM (folder for view models)

冷了相思 2024-07-22 12:31:50

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

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

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

要在不同的获取操作中填充视图模型,可以通过

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

在控制器中使用具有抽象方法 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;
}
}

Create a view model base class which has commonly required properties like result of the operation and contextual data ,you can also put current user data and roles

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

In base controller class have a method like PopulateViewModelBase() this method will fill up the contextual data and user roles.
The HasError and ErrorMessage , set these properties if there is exception while pulling data from service/db. Bind these properties on view to show error.
User roles can be used to show hide section on view based on roles.

To populate view models in different get actions , it can be made consistent by having base controller with abstract method FillModel

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

In controllers

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;
}
}
池予 2024-07-22 12:31:49

我为每个视图创建一个所谓的“ViewModel”。 我将它们放在 MVC Web 项目中名为 ViewModels 的文件夹中。 我根据它们代表的控制器和操作(或视图)来命名它们。 因此,如果我需要将数据传递到 Membership 控制器上的 SignUp 视图,我将创建一个 MembershipSignUpViewModel.cs 类并将其放入 ViewModels 文件夹中。

然后,我添加必要的属性和方法,以方便将数据从控制器传输到视图。 我使用 Automapper 从 ViewModel 到域模型,并在必要时再次返回。

这也适用于包含其他 ViewModel 类型的属性的复合 ViewModel。 例如,如果您在成员资格控制器的索引页面上有 5 个小部件,并且为每个部分视图创建了一个 ViewModel - 如何将数据从 Index 操作传递到部分视图? 您向 MyPartialViewModel 类型的 MembershipIndexViewModel 添加一个属性,并且在渲染部分时您将传入 Model.MyPartialViewModel。

通过这种方式,您可以调整部分 ViewModel 属性,而无需更改 Index 视图。 它仍然只是传入 Model.MyPartialViewModel,因此当您所做的只是向部分 ViewModel 添加属性时,您不必遍历整个部分链来修复某些内容的可能性较小。

我还将命名空间“MyProject.Web.ViewModels”添加到 web.config,以便允许我在任何视图中引用它们,而无需在每个视图上添加显式导入语句。 只是让它变得更干净一点。

I create what I call a "ViewModel" for each view. I put them in a folder called ViewModels in my MVC Web project. I name them after the controller and action (or view) they represent. So if I need to pass data to the SignUp view on the Membership controller I create a MembershipSignUpViewModel.cs class and put it in the ViewModels folder.

Then I add the necessary properties and methods to facilitate the transfer of data from the controller to the view. I use the Automapper to get from my ViewModel to the Domain Model and back again if necessary.

This also works well for composite ViewModels that contain properties that are of the type of other ViewModels. For instance if you have 5 widgets on the index page in the membership controller, and you created a ViewModel for each partial view - how do you pass the data from the Index action to the partials? You add a property to the MembershipIndexViewModel of type MyPartialViewModel and when rendering the partial you would pass in Model.MyPartialViewModel.

Doing it this way allows you to adjust the partial ViewModel properties without having to change the Index view at all. It still just passes in Model.MyPartialViewModel so there is less of a chance that you will have to go through the whole chain of partials to fix something when all you're doing is adding a property to the partial ViewModel.

I will also add the namespace "MyProject.Web.ViewModels" to the web.config so as to allow me to reference them in any view without ever adding an explicit import statement on each view. Just makes it a little cleaner.

胡渣熟男 2024-07-22 12:31:49

按类别(控制器、视图模型、过滤器等)分离类是无意义的。

如果您想为网站的 Home 部分 (/) 编写代码,请创建一个名为 Home 的文件夹,并将 HomeController、IndexViewModel、AboutViewModel 等以及 Home 操作使用的所有相关类放在那里。

如果您有共享类,例如 ApplicationController,您可以将其放在项目的根目录中。

为什么要把相关的东西(HomeController、IndexViewModel)分开,而把完全没有关系的东西(HomeController、AccountController)放在一起?


我写了一篇关于此主题的博客文章

Separating classes by category (Controllers, ViewModels, Filters etc.) is nonsense.

If you want to write code for the Home section of your website (/) then create a folder named Home, and put there the HomeController, IndexViewModel, AboutViewModel, etc. and all related classes used by Home actions.

If you have shared classes, like an ApplicationController, you can put it at the root of your project.

Why separate things that are related (HomeController, IndexViewModel) and keep things together that have no relation at all (HomeController, AccountController) ?


I wrote a blog post about this topic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文