首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ViewModel /控制器体系结构这会是最后的解决办法吗?

ViewModel /控制器体系结构这会是最后的解决办法吗?
EN

Stack Overflow用户
提问于 2014-07-29 11:05:20
回答 3查看 104关注 0票数 0

我使用了ASP.NET MVC几年,构建复杂的视图模型总是很困难的。

目前,我有一个视图,它显示了一个会议列表,我有以下问题:

  1. 每次会议都有一个雇主和一个地点。为了解决这个问题,我把模型压平了。
  2. 过滤器表单有两个字段:日期和位置。我想将过滤器值作为一个类提交,这样我就可以使用Fluent验证。
  3. 过滤器的位置列表中填充了数据库数据。
  4. 会议列表使用几个参数构建,即: pageSize和pageNumber。
  5. 模型应包括页面标题、描述和视图的其他项目.还是我应该以其他方式传递这些物品?

我一直试图遵循脂肪模型和精益控制器的想法。

因此,我有以下带有索引操作(HttpGet和HttpPost)的控制器:

(注意: Dispatcher用于发送和接收包含DTO的消息)

代码语言:javascript
复制
public partial class MeetingController : BaseController {

  public MeetingController(IDispatcher dispatcher) : base(dispatcher) {}

  [HttpGet]
  public virtual ActionResult Index(Int32 pageNumber = 1) {      
    MeetingIndexModel model = new MeetingIndexModelHandler(_dispatcher).Create();
    return View(model);
  } // Index

  [HttpPost]
public virtual ActionResult Index(MeetingIndexModel.Filter filter, Int32 p = 1) {

  if (ModelState.IsValid) {
    MeetingIndexModel model = new MeetingIndexModelHandler(_dispatcher).Update();
    return View(Views.Index, model);
  } else {
    MeetingIndexModel model = new MeetingIndexModelHandler(_dispatcher).Create();
    return View(Views.Index, model);
  }

} // Index

所以这个模型有点复杂,因为所有的需要,所以我想:

代码语言:javascript
复制
public class MeetingIndexModel {

  public IPagedList<ViewEntity> Entities { get; set; }
  public ViewFilter Filter { get; set; }
  public ViewHelper Helper { get; set; }   

  public class ViewEntity {
    public Int32 Id { get; set; }
    public String EmployeeId { get; set; }
    public String EmployeeName { get; set; }
    public String LocationName { get; set; }
    public DateTime Date { get; set; }
  }

  public class ViewFilter {
    public String Location { get; set; }
    public String Stamp { get; set; }         
  }

  public class ViewHelper {
    public Page Page { get; set; }
    public SelectList Locations { get; set; }
  }

}

实体是使用AutoMapper从DTO中扁平的会议;

Filter是用于过滤会议并在dispatcher中传递的参数。

助手包含SelectLists和其他参数..。

然后我有一个MeetingIndexHandler,它创建、更新甚至提交视图模型。例如,在创建或更新会议时,就会发生提交。

代码语言:javascript
复制
public interface IViewModelHandler<T> {
  T Create();
  T Update(T model);
  void Submit(T model);
}

当然,对于MeetingIndexModel,有一个提交方法是没有意义的。

因此,首先,在这种情况下,提交方法是没有意义的。

最后,我有了

代码语言:javascript
复制
public class MeetingIndexModelHandler : IViewModelHandler<MeetingIndexModel> {

  private IDispatcher _dispatcher;

  public ScheduleIndexModelHandler(IDispatcher dispatcher) { 
    _dispatcher = dispatcher;
  }

  public ScheduleIndexModel Create() {

    IList<MeetingDTO> meetings = _dispatcher.GetMeetings(DateTime.UtcNow, pageSize, pageNumber);

    // Map MeetingDTO to MeetingIndexModel.ViewEntity
    // Call GetLocations to get locations from database into Helper.Locations

  } // Create

  private SelectList GetLocations(String currentLocation) {

  }

}

我发现这种方法从控制器中删除了很多代码.

但有几个问题我正在寻求帮助:

  1. 提交行为并不总是有意义的;
  2. 当我创建一个模型时,我可能需要一个参数,比如pageSize和pageNumber .或者身份证或者..。 理论上,我会添加这些参数来创建作为参数的方法。但是每一种情况都是一种情况,所以我不能使用IViewModelHandler。
  3. 还有缺少的数据,比如页面标题,描述等等.我该如何把这个传递给视图呢?向ViewModel中添加另一个子类?

这种方法是否有超越工程的资格?你认为如何?

如果是,您还使用了哪些其他方法来解决这个问题?

更新1-我已经在使用服务层: dispacther

请注意,我已经有一个服务层(IDispatcher),用于从数据库获取数据,等等.例如,我像这样使用它:

代码语言:javascript
复制
FindMeetingsByDataQuery query = new FindMeetingsByDataQuery(DateTime.UtcNow);

FindMeetingsByDataReply reply = _dispacther.Send<FindMeetingsByDataReply>(query);

// Map reply.Models to my View Models

现在清楚了吗?

因此,我需要一种方法来移动这个逻辑(与服务层的通信),以构建远离控制器的视图模型。

更新2-仅使用ViewModel而不使用ViewModelHandler

另一种方法是在ViewModel中填充ViewModel而不是使用ViewModel处理程序的所有逻辑。所以在主计长里我有:

代码语言:javascript
复制
public virtual ActionResult Index(Int32 pageNumber = 1) {      
  MeetingIndexViewModel model = new MeetingIndexViewModel(_dispatcher);
  model.Create(pageNumber, 20, DateTime.UtcNow);
  return View(model);
} // Index

MeetingIndexViewModel是:

代码语言:javascript
复制
public class MeetingIndexViewModel : ViewModel {

  private IDispatcher _dispatcher;

  public IPageList<EntityModel> Entities { get; set; }
  public FilterModel Filter { get; set; }
  public HelperModel Helper { get; set; }

  public MeetingIndexViewModel(IDispatcher dispatcher) {
    _dispatcher = dispatcher;
  }

  public void Create(Int32 pageNumber, Int32 pageSize, DateTime date) {

    FindMeetingsByDateQuery query = new FindMeetingsByDateQuery(date, pageNumber, pageSize);

    FindMeetingByDateReply reply = _dispatcher.Send<FindMeetingByDateReply>(query);

    IEnumerable<MeetingIndexViewModel.EntityModel> entities = Mapper.Map<IEnumerable<FindMeetingByDateReply.MeetingModel>, IEnumerable<MeetingIndexViewModel.EntityModel>>(reply.Meetings);

    Entities = new PagedList(entities);
    Filter = new FilterModel { Date = date.ToString() };
    Helper = new HelperModel { 
      PageSize = pageSize, 
      PageNumber = pageNumber, 
      Locations = _GetLocations(x.Filter.Location) 
    };

} // Create

public void Submit() {
  // In cases where a ViewModel must be sent to the business layer for saving than it is done here. Otherwise this method is removed.
}

private SelectList _GetLocations(Int32? selected) {

  GetLocationsQuery query = new GetLocationsQuery();
  GetLocationsReply reply = _dispatcher.Send<GetLocationsReply>(query);
  return new SelectList(reply.Locations.Select(x => new { Id = x.Id, Name = x.Name }).ToList(), "Id", "Name", selected);
} // _GetLocations

public class EntityModel {
  public Int32 Id { get; set; }
  public String EmployeeId { get; set; }
  public String EmployeeName { get; set; }
  public String LocationName { get; set; }
  public DateTime Date { get; set; }
} // EntityModel

public class FilterModel {      
  public Int32? Location { get; set; }
  public String Date { get; set; }      
} // FilterModel

public class HelperModel {
  public Int32 PageSize { get; set; }
  public Int32 PageSize { get; set; }
  public SelectList Locations { get; set; }    
} // HelperModel

}

因此,通过这种方式,我删除了从控制器构建视图模型的所有逻辑,并将其传递给视图模型本身。这可以接受吗?现在它不仅仅是一台POCO。

更新3-使用ViewModel和自定义ViewModelHandler的方法

代码语言:javascript
复制
public class MeetingIndexViewModelHandler {
  public MeetingIndexViewModel Create(Int32 pageNumber, Int32 pageSize, DateTime date) {
    // Code to create the ViewModel
  }
  // Code to update the ViewModel
  // Code to submit the ViewModel

}

我认为,拥有来自接口的ViewModelHandler会使一切变得困难,因为每个视图模型都是视图模型。

更新4-将代码留在控制器

另一种方法是,如3中所示,使ViewModel只是一个POCO,而不是让Handler只将代码留在Controller中。一般来说,代码并不多,但有时可能是.

注:,根据您的经验和我发布的信息,您认为一个好的方法是什么?

EN

回答 3

Stack Overflow用户

发布于 2014-07-29 11:18:12

为此,我建议使用MVVM ()设计模式。MVVM在Model之上添加了新的包装器VM,它允许您保持模型(DTO)的干净,但是您可以添加额外的属性,如标题、描述、.这将允许您保持模型的清洁,并且它们可以在多个VM之间共享。

我会将所有操作保存在VM中,并将模型作为DTO。大多数情况下,每个动作都有自己的VM,但大多数情况下,相同控制器的每个VM都共享相同的模型。

票数 1
EN

Stack Overflow用户

发布于 2014-07-29 11:49:37

只是一个建议,如果您保留相同的接口,但将一个参数添加到接受Create基类的ViewModelArgs方法中,怎么样:

代码语言:javascript
复制
public interface IViewModelHandler<T> {
  T Create(ViewModelArgs args);
  T Update(T model);
  void Submit(T model);
}

然后,您可以使args成为ViewModel中的嵌套类。

代码语言:javascript
复制
public class SomeViewModel
{
    public class SomeViewModelArgs : ViewModelArgs
    { 
        public SomeViewModelArgs(string value) { // blah }
    }
}

像这样打电话:

代码语言:javascript
复制
SomeViewModel model = new SomeViewModelHandler(_dispatcher).Create(new SomeViewModel.SomeViewModelArgs("someValue"));

试着在这里考虑缺点,可能是在嘲笑/测试时的一些问题?值得一想,但这意味着你可以保留你的界面

编辑

但是,不确定这会给您带来什么,您不能仅仅在ViewModel上设置一个构造函数。VM不能决定从调度员那里得到什么吗?

它似乎是一个没有任何考虑它自己的图层

例如,它结束了什么:

代码语言:javascript
复制
[HttpGet]
public virtual ActionResult Index(Int32 pageNumber = 1) 
{      
    MeetingIndexModel model = new MeetingIndexModel();
    model.GetData();
    return View(model);
}

或者,如果注入了依赖项(如果您不想在构造函数中执行与数据相关的工作,那么可以让模型绑定器调用一个“后解析”方法-您也可以在其中更好地处理错误):

代码语言:javascript
复制
[HttpGet]
public virtual ActionResult Index(Int32 pageNumber = 1, MeetingIndexModel model) 
{      
    return View(model);
}

只是不知道这一层给了你什么。你能解释一下你觉得这对你有什么好处吗?也许我从你的意图中遗漏了什么

票数 0
EN

Stack Overflow用户

发布于 2014-07-29 12:48:19

  1. 你的视图模型看起来不错
  2. 我建议为页面标题/描述设置一个基类
  3. 我不喜欢IViewModelHandler方法。对我来说太普通了。我更喜欢具有明确和特定域方法名称的更具体的服务层。
  4. 还可以考虑使用依赖项注入,这样可以使代码更易于测试。 公共接口IScheduleService { ScheduleMeeting(MeetingModel会议);MeetingModel GetMeeting(int meetingId);}

更新

就我个人而言,我只会使用没有处理程序的服务层。如果您只为网站使用服务层,那么您可以直接从它返回视图模型。您的代码将更简单、更易读,而且如果需要的话,始终可以添加更多的层。这样,您就可以直接将db实体映射到视图模型。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25013831

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档