rexian

咨询电话:023-6276-4481

热门文章

联系方式

电 话:023-6276-4481

邮箱:broiling@qq.com

地址:重庆市南岸区亚太商谷6幢25-2

当前位置:网站首页 > 技术文章 > ASP.NET MVC线程问题、异常处理、自定义URL

ASP.NET MVC线程问题、异常处理、自定义URL

编辑:T.T 发表时间:2017-08-02 08:48:41
T.T

1. 创建 FileUploadViewModel

在ViewModels文件夹下新建类“FileUploadViewModel”,如下:

   1:  public class FileUploadViewModel: BaseViewModel
   2:  {
   3:      public HttpPostedFileBase fileUpload {get; set ;}
   4:  }

HttpPostedFileBase 将通过客户端提供上传文件的访问入口。

2. 创建 BulkUploadController 和Index action 方法

新建 controller“BulkUploadController”,并实现Index Action 方法,如下:

   1:  public class BulkUploadController : Controller
   2:  {
   3:          [HeaderFooterFilter]
   4:          [AdminFilter]
   5:          public ActionResult Index()
   6:          {
   7:              return View(new FileUploadViewModel());
   8:          }
   9:  }

Index方法与 HeaderFooterFilter 和 AdminFilter属性绑定。HeaderFooterFilter会确保页眉和页脚数据能够正确传递到ViewModel中,AdminFilter限制非管理员用户的访问。 
3.创建上传View

创建以上Action方法的View。View名称应为 index.cshtml,且存放在“~/Views/BulkUpload”文件夹下。

4. 设计上传View

在View中输入以下内容:

   1:  @using WebApplication1.ViewModels
   2:  @model FileUploadViewModel
   3:  @{
   4:      Layout = "~/Views/Shared/MyLayout.cshtml";
   5:  }
   6:
   7:  @section TitleSection{
   8:      Bulk Upload
   9:  }
  10:  @section ContentBody{
  11:      <div>
  12:      <a href="/Employee/Index">Back</a>
  13:          <form action="/BulkUpload/Upload" method="post" enctype="multipart/form-data">
  14:              Select File : <input type="file" name="fileUpload" value="" />
  15:              <input type="submit" name="name" value="Upload" />
  16:          </form>
  17:      </div>
  18:  }

如上,FileUploadViewModel中属性名称与 input[type="file"]的名称类似,都称为“fileUpload”。我们在Model Binder中已经讲述了名称属性的重要性,注意:在表单标签中,有一个额外的属性是加密的,会在实验结尾处讲解。

5. 创建业务层上传方法

在  EmployeeBusinessLayer中新建方法 UploadEmployees,如下:

   1:  public void UploadEmployees(List<Employee> employees)
   2:  {
   3:      SalesERPDAL salesDal = new SalesERPDAL();
   4:      salesDal.Employees.AddRange(employees);
   5:      salesDal.SaveChanges();
   6:  }<employee>
   7:  </employee>

6. 创建Upload Action 方法

创建Action 方法,并命名为 “BulkUploadController”,如下:

   1:  [AdminFilter]
   2:  public ActionResult Upload(FileUploadViewModel model)
   3:  {
   4:      List<Employee> employees = GetEmployees(model);
   5:      EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
   6:      bal.UploadEmployees(employees);
   7:      return RedirectToAction("Index","Employee");
   8:  }
   9:
  10:  private List<Employee> GetEmployees(FileUploadViewModel model)
  11:  {
  12:      List<Employee> employees = new List<Employee>();
  13:      StreamReader csvreader = new StreamReader(model.fileUpload.InputStream);
  14:      csvreader.ReadLine(); // Assuming first line is header
  15:      while (!csvreader.EndOfStream)
  16:      {
  17:          var line = csvreader.ReadLine();
  18:          var values = line.Split(',');//Values are comma separated
  19:          Employee e = new Employee();
  20:          e.FirstName = values[0];
  21:          e.LastName = values[1];
  22:          e.Salary = int.Parse(values[2]);
  23:          employees.Add(e);
  24:      }
  25:      return employees;
  26:  }

AdminFilter会绑定到Upload action方法中,限制非管理员用户的访问。

7. 创建BulkUpload链接

打开 “Views/Employee”文件夹下的 AddNewLink.cshtml 文件,输入BulkUpload链接,如下:

<a href="/Employee/AddNew">Add New</a>
  <a href="/BulkUpload/Index">BulkUpload</a>

8.运行

8.1 创建一个样本文件来测试,如图所示

8.2 运行,点击BulkUpload链接 

选择文件并点击确认

关于实验 27

为什么在实验27中不需要验证?

在该选项中添加客户端和服务器端验证需要读者自行添加的,以下是添加验证的提示:

  • 服务器端验证可使用Data Annotations。

  • 客户端验证可利用客户端的数据解释和执行jQuery的验证。必须手动设置自定义数据属性,因为并没有将Htmlhelper 方法设置为文件输入。

  • 客户端验证可编写JavaScript 代码,通过点击按钮来实现。这个方法并不是很难,由于文件输入是由输入控件完成,值可以在JavaScript中获取及验证 。

什么是 HttpPostedFileBase?

HttpPostedFileBase将通过客户端提供文件上传的访问入口,Model Binder 会在Post请求期间更新 FileUploadViewModel类中的所有属性值。我们在FileUploadViewModel内部只有一个属性,Model Binder会通过客户端设置它实现文件上传。

是否会提供多文件的输入控件?

是,有两种方法可以实现:

1. 创建多文件输入控件,每个控件有唯一的名称,FileUploadViewModel类会为每个控件创建 HttpPostedFileBase类型的属性,每个属性名称应该与控件名称匹配。

2. 创建多文件输入控件,每个控件有相同的名称,创建类型的List列表,代替创建多个HttpPostedFileBase类型的属性。

enctype="multipart/form-data" 是用来做什么的?

该属性指定了post 数据的编码类型,默认属性值是”application/x-www-form-urlencoded“

例1—登录窗体会给服务器发送以下Post 请求

   1:  POST /Authentication/DoLogin HTTP/1.1
   2:  Host: localhost:8870
   3:  Connection: keep-alive
   4:  Content-Length: 44
   5:  Content-Type: application/x-www-form-urlencoded
   6:  ...
   7:  ...
   8:  UserName=Admin&Passsword=Admin&BtnSubmi=Login

所有输入值会被作为发送的值的一部分,以”key/value“的形式发送。

当 enctype="multipart/form-data" 属性被加入Form标签中,以下post 请求会被发送到服务器。

   1:  POST /Authentication/DoLogin HTTP/1.1
   2:  Host: localhost:8870
   3:  Connection: keep-alive
   4:  Content-Length: 452
   5:  Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywHxplIF8cR8KNjeJ
   6:  ...
   7:  ...
   8:  ------WebKitFormBoundary7hciuLuSNglCR8WC
   9:  Content-Disposition: form-data; name="UserName"
  10:
  11:  Admin
  12:  ------WebKitFormBoundary7hciuLuSNglCR8WC
  13:  Content-Disposition: form-data; name="Password"
  14:
  15:  Admin
  16:  ------WebKitFormBoundary7hciuLuSNglCR8WC
  17:  Content-Disposition: form-data; name="BtnSubmi"
  18:
  19:  Login
  20:  ------WebKitFormBoundary7hciuLuSNglCR8WC--

如上所示,Form会在多部分post发送,每部分都是被分界线分割的,每部分包含单值。

如果form标签包含文件输入控件的话,enctype必须被设置为”multipart/form-data“。

为什么有时候需要设置 encType 为 “multipart/form-data”,而有时候不需要设置?

当encType  设置为”multipart/form-data“,将会实现Post数据和上传文件的功能,当然也会增加请求的size 增加,请求size 越大意味着性能越低。因此得出的最佳实践经验需要设置为默认的”application/x-www-form-urlencoded“。

为什么在实验27中创建ViewModel?

在View中已经有一个控件了,我们需要通过直接添加 HttpPostedFileBase类型的参数,并命名为”fileUpload“实现相同的结果,从而替代创建独立的ViewModel。

   1:  public ActionResult Upload(HttpPostedFileBase fileUpload)
   2:  {
   3:  }

创建 ViewModel是最好的方法,Controller应该以 ViewModel的形式给View发送数据,且数据必须来自Controller。

以上问题的解决方法

是否存在疑虑,当发送请求时,如何获取响应?

众人皆知的编程规则,程序中任何事件都是由线程执行的,请求事件也是。

Asp.net  framework 维护线程池,每次当请求发送到webserver时,会从线程池中分配空闲的线程处理此请求。这种线程被称为worker线程。

当请求处理完成,该线程无法服务其他请求时,worker 线程会被阻塞。现在我们来了解什么是线程饥饿,如果一个应用程序接收到很多请求,且处理每个请求都非常耗时。在这种情况下,我们就必须指定一个点来结束请求,当有新的请求进入状态时,没有worker 线程可使用,这种现象称为线程饥饿。

在我们的示例程序中只包含2个员工记录,而在实际使用情况下,会包含成千上万的记录,这就意味着将耗费大量的时间来处理请求。这种情况就可能导致线程饥饿.

线程饥饿的解决方法:

截至现在我们讨论的请求类型都是同步请求。如果使用异步请求来代替同步请求,那么线程饥饿的问题就得到解决了。

  • 异步请求的情况下,会分配worker线程来服务请求。

  • worker 线程初始化异步操作,并返回到线程池服务其他请求。异步操作可使用CLR 线程来继续执行。

  • 存在的问题就是,CLR 线程无法返回响应,一旦它完成了异步操作,它会通知Asp.net。

  • Webserver 再次获取一个worker线程来处理剩余的请求,并返回响应。

上述使用场景中,会获取两次worker 线程,这两次获取的线程可能相同,也可能会不同。

文件读取是I/O操作,不需要使用worker 线程处理。因此最好将同步请求转换为异步。

同步请求的响应时间能提升吗?

不可以,响应时间是相同的,线程会被释放来服务其他请求。

实验28——解决线程饥饿问题

在Asp.net MVC中会通过将同步Action方法转换为异步Action方法,将同步请求转换为异步请求。

1. 创建异步控制器

在控制器中将基类 UploadController修改为 AsynController。

   1:  {
   2:      public class BulkUploadController : AsyncController
   3:      {

2. 转换同步Action方法

该功能通过两个关键字就可实现:“async “和” await”

   1:  [AdminFilter]
   2:  public async Task<ActionResult> Upload(FileUploadViewModel model)
   3:  {
   4:      int t1 = Thread.CurrentThread.ManagedThreadId;
   5:      List<Employee> employees = await Task.Factory.StartNew<List<Employee>>
   6:          (() => GetEmployees(model));
   7:      int t2 = Thread.CurrentThread.ManagedThreadId;
   8:      EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
            上一篇 

            下一篇