52

作为一个经验丰富的 ASP.Net 开发人员,最近才开始使用 MVC,我发现自己很难将自己的思维方式从传统的“服务器控制和事件处理程序”的做事方式转变为更动态的 MVC 方式。我想我正在慢慢到达那里,但有时 MVC 的“魔法”会让我失望。

我当前的场景是创建一个网页,允许用户浏览本地文件,将其上传到服务器并重复此操作,直到他有一个文件列表可供使用。当他对文件列表(将显示在页面上的网格中)感到满意时,他将单击一个按钮来处理文件并提取一些将存储在数据库中的数据。

最后一部分并不那么重要,现在我正在努力解决一些琐碎的事情,比如建立一个文件列表,并在请求之间保留该列表。在传统方法中,这将非常简单 - 数据将保存在 ViewState 中。但是在 MVC 中,我需要在控制器和视图之间传递数据,但我并不完全了解它应该如何工作。

我想我最好发布我相当不完整的编码尝试来解释问题。

为了保留我的文件列表数据,我创建了一个视图模型,它基本上是一个文件类型列表,以及一些额外的元数据:

public class ImportDataViewModel
{
    public ImportDataViewModel()
    {
        Files = new List<ImportDataFile>();
    }

    public List<ImportDataFile> Files { get; set; }
...

在视图中,我有一个用于浏览和上传文件的表单:

<form action="AddImportFile" method="post" enctype="multipart/form-data">
  <label for="file">
    Filename:</label>
  <input type="file" name="file" id="file" />
  <input type="submit" />
  </form>

该视图使用视图模型作为其模型:

@model MHP.ViewModels.ImportDataViewModel

这会将文件发送到我的操作:

public ActionResult AddImportFile(HttpPostedFileBase file, ImportDataViewModel importData)
    {

        if (file.ContentLength > 0)
        {
            ImportDataFile idFile = new ImportDataFile { File = file };
            importData.Files.Add(idFile);
        }

        return View("DataImport", importData);
    }

此操作返回 DataImport 页面的视图以及包含文件列表的 viewmodel 实例。

这在一定程度上工作得很好,我可以浏览文件并上传它,我可以看到动作中的视图模型数据,然后如果我在视图中放置一个断点并调试“this.Model”,一切都是美好的。

但是,如果我尝试上传另一个文件,在 AddImportFile 操作中放置断点时,importData 参数为空。所以视图显然没有将其模型的当前实例传递给动作。

在我经历过的MVC示例中,模型实例被“神奇”地作为参数传递给了action方法,那为什么现在是空的呢?

我认为真正的问题是我对 MVC 的理解有限,并且可能有一个非常简单的解决方案。无论如何,如果有人能指出我正确的方向,我将非常感激。

4

2 回答 2

49

自从我发布这个问题以来已经有一段时间了,我对 MVC 的一点经验和知识使这个问题变得非常有趣。尽管如此,我还是收到了一些非常有用的意见,最终让我找到了解决方案并获得了对 MVC 的一些见解。

首先让我失望的是,您可以拥有一个带有强类型对象作为参数的控制器,如下所示:

public ActionResult DoSomething(MyClass myObject)...

这个对象来自同一个控制器:

...
return View(myObject);
...

这让我相信该对象在这两个步骤中都存在,并且我可以期望您可以将其发送到视图,执行某些操作,然后“神奇地”将其再次返回到控制器。

在阅读了有关模型绑定的内容后,我明白这当然不是这种情况。视图完全是死的和静态的,除非您将信息存储在某个地方,否则它就消失了。

回到问题,从客户端选择和上传文件,并建立这些文件的列表以显示,我意识到通常在 MVC 中存储请求之间的信息有三种方式:

  1. 您可以将信息存储在视图中的表单字段中,稍后将其发布回控制器
  2. 您可以将其保存在某种存储中,例如文件或数据库
  3. 您可以通过访问存在于整个请求中的对象(例如会话变量)将其存储在服务器内存中

就我而言,我基本上有两种类型的信息要持久化: 1. 文件元数据(文件名、文件大小等) 2. 文件内容

“按书本”的方法可能是将元数据存储在表单字段中,并将文件内容存储在文件或数据库中。但还有另一种方式。因为我知道我的文件非常小,而且只有少数几个,而且这个解决方案永远不会部署在服务器场或类似的地方,所以我想探索会话变量的#3 选项。这些文件在会话之外持续存在也没有什么意义——它们被处理和丢弃,所以我不想将它们存储在我的数据库中。

阅读这篇优秀文章后: 使用动态访问 ASP.NET 会话数据

我被说服了。我只是按照文章中的描述创建了一个 sessionbag 类,然后我可以在我的控制器中执行以下操作:

    [HttpPost]
    public ActionResult AddImportFile(HttpPostedFileBase file)
    {

        ImportDataViewModel importData = SessionBag.Current.ImportData;
        if (importData == null) importData = new ImportDataViewModel();

        if (file == null)
            return RedirectToAction("DataImport");

        if (file.ContentLength > 0)
        {
            ImportDataFile idFile = new ImportDataFile { File = file };
            importData.Files.Add(idFile);
        }

        SessionBag.Current.ImportData = importData;

        return RedirectToAction("DataImport");
    }

我完全清楚,在大多数情况下,这将是一个糟糕的解决方案。但是对于文件占用的服务器内存的几 kb 以及这一切的简单性,我认为它对我来说非常好。

使用 SessionBag 的额外好处是,如果用户输入了不同的菜单项然后返回,文件列表仍然存在。例如,如果选择表单字段/文件存储选项时,则不是这样的情况。

最后,我意识到 SessionBag 很容易被滥用,因为它使用简单。但是,如果您将它用于它的用途,即会话数据,我认为它可以成为一个强大的工具。

于 2012-06-03T18:23:59.887 回答
11

关于上传

1)也许考虑一个带有 HTML 的 AJAX 上传器,以允许您的用户在将它们发送到服务器之前选择多个文件。这个 BlueImp Jquery AJAX 文件上传器非常棒,有一个非常棒的 api:Blueimp Jquery File Upload。它将允许用户拖放或多选多个文件并编辑文件顺序、包含/排除等。然后当他们满意时,他们可以按上传发送到您的控制器或上传处理程序以在服务器端进行处理。

2)你可以让每次上传都持久化到数据库,尽管你会重新加载整个页面并编写一些额外的视图模型和剃须刀代码来实现列表效果。这可能不会响应...

关于保持状态 WebForms/MVC

在请求之间保持状态有点黑魔法和巫术。进入 ASP.NET MVC 时,了解 Web 应用程序使用请求和响应进行通信。所以去拥抱无状态的网络并从那里发展吧!当您的模型通过您的控制器发布时,它与控制器中的任何变量一起消失了!但是,在它消失之前,您可以将其内容存储在数据库中以供以后检索。

Web 应用程序不能像桌面应用程序那样保持真实状态。ajax 框架有很多方法,人们使用一些巫毒工具来模拟 HTTP 环境中的状态。对状态的模拟实际上只是对状态的错误模仿。ASP.NET Web 窗体试图通过向开发人员隐藏 HTTP 的无状态特性来尽可能地模拟状态。当您尝试将自己的 AJAX 代码与 Web 表单标记代码及其自己的 Ajax 框架结合使用时,您可能会遇到很多麻烦。

我真的很高兴你正在学习 MVC

抛开所有的玩笑不谈,如果你有 MVC/HTTP/Stateless 的心态,很容易将这些模式应用到其他超级流行的框架,如 Ruby on Rails、SpringMVC (java)、Django (python)、CakePHP 等......这种简单的知识转移将帮助您成为更好的开发人员并真正精通 Ajax。

我真的很高兴你正在学习 MVC 3,我在一些非常大的公司实习过,这些公司有这些疯狂的大型 ASP.NET Web 窗体项目,代码到处乱飞,只是为了改变数据库中的一些数值(-_-') 感觉就像我在用剪刀编织婴儿的袜子。一个简单的错误举动,一切都崩溃了。感觉就像在 PHP 中开发一样,你出汗了,并不确定发生了什么以及发生在哪一行。调试和更新几乎是不可能的。

于 2012-05-25T14:45:11.047 回答