Asp.Net MVC<六>:Controller、Action 待续 控制器

抽象类Controller

Visual Studio的向导创建的Controller类型继承自抽象类Controller。

  1. 它是ControllerBase的子类。
  2. 实现了IController 和IAsyncController接口。支持同步和异步两种执行方式,默认采用异步。
  3. 实现了代表MVC 5种过滤器的接口。
  4. 实现了IAsyncManagerContainer接口,提供一个AsyncManager对象位异步操作的执行提供参数传递、操作计数和超时控制等功能。
  5. 实现了IDisposable接口,使得Controller激活系统在Controller执行结束后可以完成相应的资源回收工作。
 public abstract class Controller : ControllerBase, 
	IActionFilter, 
	IAuthenticationFilter, 
	IAuthorizationFilter, 
	IExceptionFilter, 
	IResultFilter, 
	
	IDisposable, 	

	IAsyncController, 
	IController, 
	IAsyncManagerContainer
{
    //……
}

ControllerBase

TempData仅仅用于存储临时数据,并且设置的变量在第一次被读取后就会被移除。

ViewData和ViewBag时同一份数据的不同表现形式,后者是一个动态对象。

public abstract class ControllerBase : IController
{
    protected ControllerBase();

    public ControllerContext ControllerContext { get; set; }

    public TempDataDictionary TempData { get; set; }

    public bool ValidateRequest { get; set; }

    public IValueProvider ValueProvider { get; set; }

    public dynamic ViewBag { get; }

    public ViewDataDictionary ViewData { get; set; }

    protected virtual void Execute(RequestContext requestContext);

    protected abstract void ExecuteCore();

    protected virtual void Initialize(RequestContext requestContext);
}

同步执行Or异步执行

ASP.NET是基于线程池来处理请求的。如果请求处理操作耗时较短,那么工作线程处理完毕后可以及时地被释放到线程池中以用于对下一个请求的处理。但是对于比较耗时的操作来说,意味着工作线程将被长时间被某个请求独占,如果这样的操作访问比较频繁,在高并发的情况下意味着线程池中将可能找不到空闲的工作线程用于及时处理最新抵达请求。

如果我们采用异步的方式来处理这样的耗时请求,工作线程可以让后台线程来接手,自己可以及时地被释放到线程池中用于进行后续请求的处理,从而提高了整个服务器的吞吐能力。值得一提的是,异步操作主要用于I/O绑定操作(比如数据库访问和远程服务调用等),而非CPU绑定操作,因为异步操作对整体性能的提升来源于:当I/O设备在处理某个任务的时候,CPU可以释放出来处理另一个任务。如果耗时操作主要依赖于本机CPU的运算,采用异步方法反而会因为线程调度和线程上下文的切换而影响整体的性能。

Controller的异步执行

IController接口中的Execute方法是以同步的方式执行的,而IAsyncController接口则提供了异步执行的方式。

public interface IAsyncController : IController
{

    IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state);

    void EndExecute(IAsyncResult asyncResult);
}

Controller类型具有一个布尔类型的属性DisableAsyncSupport,默认情况下总是返回False,即支持以异步方式执行Controller。

而由于Controller类型实现了IAsyncController接口,所以Controller的执行总是通过调用BeginExecute/EndExecute方法以异步方式来执行。属性DisableAsyncSupport则决定BeginExecute方法内部是以同步还是异步的方式调用Execute/BeginExecuteCore方法。

public abstract class Controller:……
{
    //……

    protected virtual bool DisableAsyncSupport { get { return false; } }

    protected virtual IAsyncResult BeginExecute(RequestContext requestContext
       , AsyncCallback callback, object state)
    {
        if (this.DisableAsyncSupport)
        {
            //通过调用Execute方法同步执行Controller
        }
        else
        {
            //通过调用BeginExecuteCore/EndExecuteCore方法异步执行Controller
        }
    }
}

Action的异步执行

URL:http://localhost:61771/test/Article, 读取文件并返回内容。

MVC4.0 之前,通过AsyncController类型实现异步执行。

public class TestController : AsyncController
{
    public void ArticleAsync(string name)
    {
        AsyncManager.OutstandingOperations.Increment();
        Task.Factory.StartNew(() =>
        {
            string path = string.Format(@"articles{0}.html", name);
            path = ControllerContext.HttpContext.Server.MapPath(path);
            using (StreamReader reader = new StreamReader(path))
            {
                AsyncManager.Parameters["content"] = reader.ReadToEnd();
            }
            AsyncManager.OutstandingOperations.Decrement();
        });
    }
    public ActionResult ArticleCompleted(string content)
    {
        return Content(content);
    }
}

  

在MVC4.0后 提供了新的异步Action方法定义方式

public class TestController : Controller
{
    public Task<ActionResult> Article(string name)
    {
        string path = string.Format(@"articles{0}.html", name);
        path = ControllerContext.HttpContext.Server.MapPath(path);
        StreamReader reader = new StreamReader(path);

        return reader.ReadToEndAsync().ContinueWith<ActionResult>(task => 
        {
            reader.Close();
            return Content(task.Result);
        });
    }
}

另一个版本

public async Task<ActionResult> Article(string name)
{
    string path = string.Format(@"articles{0}.html", name);
    path = ControllerContext.HttpContext.Server.MapPath(path);
    using (StreamReader reader = new StreamReader(path))
    {
        return Content(await reader.ReadToEndAsync());
    }
}