ASP.Net MVC和状态 – 如何保持请求之间的状态
作为一位刚刚开始使用MVC的相当有经验的ASP.Net开发人员,我发现自己正在努力将自己的思维从传统的“服务器控制和事件处理程序”方式转变为更加dynamic的MVC方式。 我想我正在慢慢到达那里,但是有时MVC“魔术”会把我抛弃。
我目前的scheme是创build一个网页,允许用户浏览本地文件,将其上传到服务器,并重复这一点,直到他有一个文件列表的工作。 当他对文件列表感到满意时(他将在页面上显示一个网格),他将点击一个button来处理这些文件并提取一些将被存储在数据库中的数据。
最后一部分不是那么重要,现在我正忙于build立一个文件列表,并在请求之间保存这个列表。 在传统的方法中,这将非常简单 – 数据将被保留在ViewState中。 但是在MVC中,我需要在控制器和视图之间传递数据,而我并不完全知道这应该如何工作。
我想我更好地发布我的编码这个不完整的尝试来解释这个问题。
为了保留我的文件列表数据,我创build了一个基本上是文件types列表的视图模型,以及一些额外的元数据:
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实例。
这很好地工作到某一点,我可以浏览一个文件,并上传它,我可以看到行动里面的viewmodel数据,然后,如果我把一个断点内的视图和debugging“this.Model”,一切都是精细。
但是,如果我尝试上传另一个文件,在AddImportFile操作中放置一个断点时,importData参数是空的。 所以这个观点显然不会将其模型的当前实例传递给动作。
在MVC示例中,我已经通过了模型实例作为参数“神奇地”传递给action方法,为什么它现在是空的?
我认为真正的问题是我对MVC的理解有限,并且可能有一个非常简单的解决scheme。 无论如何,如果有人能指出我正确的方向,我将非常感激。
自从我发布这个问题已经有一段时间了,这个问题在我对MVC的一些经验和知识上是相当有色彩的。 不过,我收到了一些非常有用的input,最终使我find了一个解决scheme,并获得了MVC的一些见解。
什么把我扔在第一位,是你可以有一个强types的对象作为参数的控制器,如下所示:
public ActionResult DoSomething(MyClass myObject)...
这个对象来自同一个控制器:
... return View(myObject); ...
这使我相信,这个对象贯穿了这两个步骤,而且我总觉得可以把它发送到视图,做一些事情,然后“神奇地”把它重新带回控制器。
在阅读了模型绑定之后,我明白这当然不是这种情况。 视图是完全死的和静态的,除非你将信息存储在某个地方,否则它不见了。
回到从客户端select和上传文件的问题,并build立这些文件的列表来显示,我意识到,通常有三种方式来存储MVC请求之间的信息:
- 您可以将信息存储在视图中的表单字段中,稍后将其发回到控制器
- 您可以将其保存在某种存储中,例如文件或数据库
- 您可以通过访问遍及请求的对象(例如会话variables)将其存储在服务器内存中
在我的情况下,我基本上有两种types的信息要坚持:1.文件元数据(文件名,文件大小等)2.文件内容
“通过书”的方法可能是将元数据存储在表单字段中,并将文件内容存储在文件或db中。 但是也有另一种方法。 因为我知道我的文件非常小,只有less数文件,而且这个解决scheme永远不会被部署在服务器场或类似的地方,所以我想探索会话variables的#3选项。 这些文件在会话之外持续存在也不是很有趣 – 它们被处理和丢弃,所以我不想将它们存储在我的数据库中。
阅读这篇优秀的文章之后: 使用Dynamics访问ASP.NET会话数据
我深信。 我只是按照文章中的描述创build了一个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"); }
我完全知道,在大多数情况下,这将是一个不好的解决scheme。 但是对于less量的服务器内存的文件占用和简单的一切,我认为这对我来说非常好。
使用SessionBag的额外好处是,如果用户input了不同的菜单项然后又返回,那么文件列表将仍然存在。 这不会是这种情况,例如,当select表单域/文件存储选项。
作为最后的评论,我认识到SessionBag很容易被滥用,因为使用简单。 但是,如果你使用它的意思,即会话数据,我认为它可以是一个强大的工具。
关于上传
1)也许考虑一个带有HTML的AJAX上传器,允许用户在发送到服务器之前select多个文件。 这个BlueImp Jquery AJAXfile upload器是一个相当不错的API: Blueimp Jqueryfile upload 。 它将允许用户拖放或多选几个文件,编辑文件顺序,包括/排除等。然后当他们感到高兴时,他们可以按上传发送到您的控制器或上传处理程序在服务器端进行处理。
2)你可以使每个上传保持到数据库,但你会重新加载整个页面,并写一些额外的视图模型和razor代码,以实现上市的效果。 这可能不会有响应…
关于保持状态WebForms / MVC
保持请求之间的状态有点黑魔法和巫术。 进入ASP.NET MVC时,要深入了解Web应用程序使用请求和响应进行通信。 所以去拥抱networking是无国籍的,并从那里发展! 当你的模型通过你的控制器发布时,它与控制器中的任何variables一起消失 ! 然而,在它消失之前,您可以将其内容存储在数据库中供以后检索。
Web应用程序无法像桌面应用程序那样保持真实状态 ajax框架有很多种方法,人们用来模拟HTTP环境中的一些voodoo工具。 而对状态的模拟实际上只是对有状态的虚假模仿。 ASP.NET Web窗体试图通过隐藏开发人员的无状态HTTP来尽可能地模拟状态。 尝试将自己的AJAX代码与Web Forms标记代码及其自己的Ajax框架结合使用时,可能会遇到许多麻烦。
我很高兴你正在学习MVC
所有的笑话,如果你得到了MVC / HTTP /无状态的思路,将它应用到其他超级stream行的框架,如Ruby on Rails,SpringMVC(Java),Django(python),CakePHP等将是非常容易的…这种简单的知识转移将帮助你成为一个更好的开发人员,并且非常擅长Ajax。
我真的很高兴你正在学习MVC 3,我已经在一些非常大的公司做了一些实习,这些公司拥有这些疯狂的大型ASP.NET Web Forms项目,并且随处可以添加代码,只是为了改变数据库中的一些数字值(-_-')感觉像是用剪刀织一个婴儿的袜子。 一个错误的举动,一切都打破了。 这感觉就像在PHP开发,你出汗,不知道发生了什么事,什么行。 debugging和更新几乎是不可能的。