加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – ASP.NET MVC – 正确使用View Model和Command模

发布时间:2020-12-16 03:30:21 所属栏目:asp.Net 来源:网络整理
导读:我已经编写ASP.NET MVC应用程序一段时间了,我发现它们是使用命令模式的好地方:我们将每个用户请求表示为命令 – 一组输入参数 – 然后处理此命令(处理包括验证和其他域逻辑),结果将发送回用户. 我在我的应用程序中使用的另一件事是视图模型.我发现它们是将
我已经编写ASP.NET MVC应用程序一段时间了,我发现它们是使用命令模式的好地方:我们将每个用户请求表示为命令 – 一组输入参数 – 然后处理此命令(处理包括验证和其他域逻辑),结果将发送回用户.

我在我的应用程序中使用的另一件事是视图模型.我发现它们是将数据传递给视图比使用域对象作为模型或填充ViewData / ViewBag更方便的方式.

这两个概念非常适合将显示给用户的数据与用户输入及其处理分开,但它们在ASP.NET MVC中并不完全一致.

假设我想在开发一个简单的网上商店时使用命令和视图模型,用户可以在其中查看产品,并可以通过提供产品名称和电子邮件地址来订购产品:

class ProductViewModel 
{
    public ProductViewModel(int id) { /* init */ }
    public int Id { get; set; }
    public string Name { get; set; }
    // a LOT of other properties (let's say 50)
}

class OrderProductCommand
{
    public int ProductId { get; set; }

    [Required(ErrorMessage = "Name not specified")]
    public string Name { get; set; }

    [Required(ErrorMessage ="E-Mail not specified")]
    public string Email { get; set; }

    public CommandResult Process() { /* validate,save to DB,send email,etc. */ }
}

在浏览教程时,我看到人们提出了几种方法.

选项1

控制器:

[HttpGet]
public ActionResult Product(int id)
{
    return View(new ProductViewModel(id));
}

[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
    if (ModelState.IsValid)
    {
        var result = command.Process();
        if(result.Success)
            return View("ThankYou");
        else
            result.CopyErrorsToModelState(ModelState);
    }
    return Product(command.Id);
}

视图:

@using (Html.BeginForm())
{
    @Html.Hidden("ProductId",Model.Id)
    @Html.TextBox("Name")
    @Html.TextBox("Email")
    <input type="submit" value="Place order" />
}

优点:视图模型和命令相互分离,HttpPost方法看起来很干净

缺点:我无法使用方便的HTML帮助程序,如@ Html.TextBoxFor(model => model.Email),我无法使用客户端验证(参见my other question)

选项2

我们将Id,Name和Email连同其验证属性从命令复制到viewModel.

控制器:

[HttpPost]    
public ActionResult Product(ProductViewModel viewModel)
{
        var command = new OrderProductCommand();
        command.Id = viewModel.Id;
        command.Name = viewModel.Name;
        command.Email = viewModel.Email;        
        if (ModelState.IsValid)
        // ...
}

视图:

@Html.TextBoxFor(m => m.Email)
...

优点:所有选项1缺点都消失了

缺点:复制属性似乎不方便(如果我有50个?),在视图模型中验证名称和电子邮件(它应该在域逻辑的其余部分所在的命令中完成),模型作为POST参数(请参阅下面)

选项3

我们使命令成为viewModel的属性.

控制器:

[HttpPost]
public ActionResult Product(ProductViewModel viewModel)
{
        var command = viewModel.Command;
        if (ModelState.IsValid)
        // ...
}

视图:

@Html.TextBoxFor(m => m.Command.Email)
...

优点:所有选项1缺点都消失了

缺点:视图模型应该只包含显示给用户的数据(并且不显示命令),模型作为POST参数(见下文)

我不喜欢选项2和3的是我们使用视图模型作为POST方法参数.此方法用于处理用户输入(在这种情况下仅隐藏2个字段1),并且该模型包含50个我将永远不会在此方法中使用且始终为空的属性.更不用说为视图模型创建一个空构造函数的必要性,只是为了处理这个POST请求以及为每个POST请求创建大型视图模型对象时不必要的内存消耗.

我的问题是(这就像有史以来最长的问题,我知道):是否存在一个秘密的选项4,用于正确使用命令和查看模型,这些模型具有所有优点,而没有其他优点?或者我是偏执狂,这些缺点并不重要,可以忽略不计?

解决方法

看起来像唯一的另一种不错的方式是使用局部视图来渲染表单并使用OrderProductCommand作为视图模型.

Product.cshtml:

@model ProductViewModel
...
@Html.Partial("Product_OrderForm",new OrderProductCommand { ProductId = Model.Id })
...

Product_OrderForm.cshtml:

@model OrderProductCommand
...
@using (Html.BeginForm("Product","Home"))
{
    @Html.HiddenFor(cmd => cmd.ProductId)
    @Html.TextBoxFor(cmd => cmd.Name)
    @Html.TextBoxFor(cmd => cmd.Email)
    <input type="submit" value="Place order" />
}
...

这样就不需要在视图模型和业务对象之间创建数据映射,并且控制器代码可以像在选项1中一样保持干净:

[HttpGet]
public ActionResult Product(int id)
{
    return View(new ProductViewModel(id));
}

[HttpPost]
public ActionResult Product(OrderProductCommand command)
{
    // process command...
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读