ASP.NET MVC - 漫谈ActionMethodDispatcher

来源:互联网 发布:新东方网络课 编辑:程序博客网 时间:2024/06/07 04:51

跳过ASP.NET MVC中所有的路由映射和各种有的没的缓存机制,一个个请求从发送到处理,最终是由Action去执行的,而在ASP.NET MVC中,Action的最终执行者叫做ActionMethodDispacher。

在这篇文章中,我想从无到有地建立一个Dispatcher,顺便窥探一下ASP.NET MVC中Action的执行机制。

在没有看源码之前,我觉得这个Dispatcher没有什么了不起,不就是个动态函数调用嘛,使用反射调用不就就行了,几行代码随便搞定:

    public class ReflectionDispatcher    {        public MethodInfo MethodInfo {get;set;}        public ReflectionDispatcher(MethodInfo methodInfo)        {            MethodInfo = methodInfo;        }        public object Execute(TestClass instance, object[] parameters)        {            return MethodInfo.Invoke(instance, parameters);        }    }

多么简单,简直就是一目了然,为了这么点功能,建一个类简直就是多余!

但是,如果ASP.NET MVC中的ActionMethodDispacher也是这样实现的话,那么...嘿嘿,估计会被人骂的很惨吧。

原因是这个ReflectDispacher的性能非常低下,不是因为代码写得太差,只是因为反射调用先天不足,本来就有性能的瓶颈。

关于如何高效动态调用函数,这里就不多说了,详见:http://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker

下面我试着仿照ASP.NET MVC的ActionMethodDispacher来建一个性能高点的Dispacher,至于性能可以高多少,不妨等下做个小试验。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Reflection;using System.Linq.Expressions;namespace MvcConsole{    public class ActionMethodDispatcher    {        private delegate object ActionExecutor(TestClass instance, object[] parameters);        private delegate void VoidActionExecutor(TestClass instance, object[] parameters);        private ActionExecutor _executor;        private static ActionExecutor BuildActionExecutor(MethodInfo method)        {            ParameterExpression instanceParameter = Expression.Parameter(typeof(TestClass), "instance");            ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");            List<Expression> parameters = new List<Expression>();            ParameterInfo[] parameterInfos = method.GetParameters();            for (int i = 0; i < parameterInfos.Length; i++)            {                ParameterInfo paramInfo = parameterInfos[i];                BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));                UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);                parameters.Add(valueCast);            }            MethodCallExpression methodCall = Expression.Call(instanceParameter, method, parameters);            return                methodCall.Type == typeof(void) ?                WrapVoidAction(Expression.Lambda<VoidActionExecutor>(methodCall, instanceParameter, parametersParameter).Compile()) :                Expression.Lambda<ActionExecutor>(methodCall, instanceParameter, parametersParameter).Compile();        }        public ActionMethodDispatcher(MethodInfo methodInfo)        {            _executor = BuildActionExecutor(methodInfo);        }        public object Execute(TestClass instance, object[] parameters)        {            return _executor(instance, parameters);        }        private static ActionExecutor WrapVoidAction(VoidActionExecutor executor)        {            return (instance, parameters) =>            {                executor(instance, parameters);                return null;            };        }    }}

从这个代码中,我们明显可以看到,这个Dispacher貌似要“胖”很多。

下面就来比较下两个Dispatcher的性能:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Reflection;using System.Linq.Expressions;using System.Diagnostics;namespace MvcConsole{    public class TestClass    {        public String State { get; set; }        public TestClass()        {            State = "Init";        }        public void PrintObjects(String para1, String para2)        {        }    }    class Program    {        static void Main(string[] args)        {            Stopwatch stopWatch = new Stopwatch();            int times = 10000000;            var dispatcher = new ActionMethodDispatcher(typeof(TestClass).GetMethod("PrintObjects"));            var testClass = new TestClass();            testClass.State = "Expression Call";            stopWatch.Start();            for (int i = 0; i < times; i++)            {                dispatcher.Execute(testClass, new object[] { "Hello", "World" });            }            stopWatch.Stop();            Console.WriteLine("Expression Call use: " + stopWatch.Elapsed);            var mydispatcher = new RelectionDispatcher(typeof(TestClass).GetMethod("PrintObjects"));            testClass.State = "Reflect Call";            stopWatch.Reset();            stopWatch.Start();            for (int i = 0; i < times; i++)            {                mydispatcher.Execute(testClass, new object[] { "Hello", "World" });            }            stopWatch.Stop();            Console.WriteLine("Reflect Call use: " + stopWatch.Elapsed);        }    }}

运行结果:

Expression Call use: 00:00:00.3916032
Reflect Call use: 00:00:09.4091986

ActionMethodDispatcher的性能要比ReflectDispatcher的性能高20多倍。好吧,这次Reflect的调用真是完败啊,为了这20多倍的性能,我们就是再多写20多倍的代码也在所不惜啊。




原创粉丝点击