作为一个刚刚开始使用MVC的经验丰富的 ASP.Net 开发人员,我发现自己有点努力改变我的思维方式,从传统的"服务器控制和事件处理程序"做事方式,到更动态的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);
}
此操作将返回"数据导入"页的视图以及包含文件列表的视图模型实例。
这在一定程度上工作得很好,我可以浏览文件并上传它,我可以在操作中看到视图模型数据,然后如果我在视图中放置一个断点并调试"this.模型",一切都很好。
但是,如果我尝试上传另一个文件,则在 AddImportFile 操作中放置断点时,importData 参数为空。因此,视图显然没有将其模型的当前实例传递给操作。
在我经历过的 MVC 示例中,模型实例作为参数"神奇地"传递给操作方法,那么为什么它现在为空呢?
我认为真正的问题是我对MVC的理解有限,并且可能有一个非常简单的解决方案。无论如何,如果有人能指出我正确的方向,我将不胜感激。
自从我发布这个问题以来已经有一段时间了,这个问题被我对 MVC 的一点经验和知识所影响。尽管如此,我还是收到了一些非常有用的输入,最终导致我找到了一个解决方案,并对MVC有一些了解。
首先让我失望的是,你可以有一个带有强类型对象的控制器作为参数,如下所示:
public ActionResult DoSomething(MyClass myObject)...
此对象源自同一控制器:
...
return View(myObject);
...
这让我相信对象贯穿了这两个步骤,并且我可以以某种方式期望您可以将其发送到视图,执行某些操作,然后"神奇地"将其再次返回控制器。
在阅读了模型绑定之后,我明白了当然不是这样。视图完全是死的和静态的,除非你将信息存储在某个地方,否则它就会消失。
回到问题,即从客户端选择和上传文件,并建立要显示的这些文件的列表,我意识到通常有三种方法可以在 MVC 中的请求之间存储信息:
- 您可以将信息存储在视图的表单字段中,并在以后将其发布回控制器
- 您可以将其保存在某种存储中,例如文件或数据库
- 您可以通过处理整个请求中的对象(例如会话变量)将其存储在服务器内存中
就我而言,我基本上有两种类型的信息需要保留: 1. 文件元数据(文件名、文件大小等) 2. 文件内容
"按书"的方法可能是将元数据存储在表单字段中,将文件内容存储在文件或数据库中。但还有另一种方式。由于我知道我的文件很小,而且只有几个,并且此解决方案永远不会部署在服务器场或类似环境中,因此我想探索会话变量的#3选项。这些文件在会话之后持久化也不有趣 - 它们被处理和丢弃,所以我不想将它们存储在我的数据库中。
看完这篇优秀的文章:使用动态访问 ASP.NET 会话数据
我深信不疑。我只是按照本文中所述创建了一个会话包类,然后我可以在我的控制器中执行以下操作:
[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很容易被滥用,因为使用简单。但是,如果您将其用于其目的,即会话数据,我认为它可以成为一个强大的工具。
关于上传
1)也许可以考虑一个带有HTML的AJAX上传器,以允许您的用户在将文件发送到服务器之前选择多个文件。这个BlueImp Jquery AJAX文件上传器非常棒,有一个非常棒的API:Blueimp Jquery文件上传。它将允许用户拖放或多选多个文件并编辑文件顺序,包括/排除等。然后,当他们满意时,可以按上传发送到您的控制器或上传处理程序,以便在服务器端进行处理。
2)您可以使每次上传都保留到数据库中,尽管您将重新加载整个页面并编写一些额外的视图模型和剃刀代码以实现列表效果。这可能不会有响应...
关于保持状态 WebForms/MVC
在请求之间保持状态有点黑魔法和巫毒教。进入 MVC ASP.NET 时,请了解 Web 应用程序使用请求和响应进行通信。因此,将网络视为无状态并从那里开始开发!当您的模型通过控制器发布时,它会与控制器中的任何变量一起消失!但是,在它消失之前,您可以将其内容存储在数据库中以供以后检索。
Web 应用程序不能像桌面应用程序那样保持真实状态。ajax框架有很多方法,以及人们用来模拟HTTP环境中状态的一些voodoo工具。对状态的模拟实际上只是对状态性的虚假模仿。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 Forms项目,代码到处飞,只是为了改变数据库中的一些数值(-_-')感觉就像我在用剪刀编织婴儿的袜子。一个简单的错误举动,一切都崩溃了。感觉就像在PHP中开发一样,你出来时汗流浃背,不确定发生了什么,在哪条线上。调试和更新几乎是不可能的。