Spiga

尽可能摆脱对HttpContext的依赖

2009-03-09 09:17 by Jeffrey Zhao, 7346 visits, 网摘, 收藏, 编辑

我们继续《ASP.NET MVC单元测试最佳实践》,今天主要谈论HttpContext的依赖问题。

在ASP.NET中进行单元测试的天敌便是HttpContext,它是ASP.NET的核心,极端复杂,却无法进行Mock1——可见微软能够写出那么庞大的ASP.NET框架真不那么容易。现在这个状况改善了不少,因此大家已经可以使用System.Web.Abstractions.dll了,这个程序集中提供了对于HttpContext的抽象,也就是HttpContextBase抽象类。因此在ASP.NET MVC中,各种组件均依赖于HttpContextBase而不是HttpContext。这是一个优秀的做法,大家以后可以尽可能地摆脱HttpContext了。

不过这似乎又是一个悖论。虽然已经可以对HttpContext进行Mock(这点增强了可测试性),但是过度依赖HttpContext对于单元测试来说也是一个伤害。这是HttpContext对象的天性所致:它实在太复杂了。您应该已经察觉到,这是个集万千宠爱于一身的对象,从请求,回复,应用程序,缓存……几乎包含了Web应用程序需要的所有信息。如果要测试一个依赖于HttpContext的方法,您势必要为HttpContext的Mock对象填充各种信息——其复杂程度视业务而定。而且,Mock关注的是“行为”,也就是说它关注的是做一件事情所使用“路径”。那么如果做一件事情可以采用多个路径又会怎样?是否需要在测试之前准备好所有的路径,并且验证被测试的代码“采用了,并仅仅采用了其中一条路径”?因此,Stub慢慢进入人们的视线。Stub关注的是“状态”……这就是另一个话题了,还会涉及到采用Record & Replay还是Arrange-Act-Assert方式来进行单元测试,暂且不提。

之前谈到对视图进行单元测试时,老赵曾经谈起在视图中应该只使用ViewData中的数据。这不是第一次说起要放弃HttpContext了,自从有了“抽象”这一有利武器后,一切“不和谐”因素都能够被分离。试想在MVP模式中,View和Presenter都使用各自的抽象进行交互,一切Web控件,HttpContext等对象都不复存在了,大家眼中只有“数据”和“模型”。同样,在ASP.NET MVC的Action方法中,也不应该使用HttpContext,这是基于良好的“可测试性”而考虑的。您可能会想,现在的HttpContextBase对象已经可以Mock了啊。没错,它的确“可以”,但是这样做会引起单元测试代码的膨胀,因为测试代码中的相当部分必须关注在测试数据的准备,而不是被测试的功能上。对于一个Action方法来说,它关注的应该是用户与业务逻辑的交互,而不是“如何把HTTP请求转化为可用的数据”。其实说到底,还是要“分离关注点”。

在ASP.NET MVC中负责“转化数据”的层次为Model Binder。关于这一点,现有的“示例”大都关注把Form或QueryString中的数据转化为Action参数上,不过Model Binder可用的地方其实更多。例如在《最佳实践》的代码中,原本AccountController的Delete方法实现如下:

public ActionResult Delete(string userName)
{
    this.MiddleTier.UserManager.Delete(userName);

    Uri urlReferrer = this.Request.UrlReferrer;
    return this.Redirect(urlReferrer.ToString());
}

在删除了指定对象之后,页面将跳转到Url Referrer地址中。在上面的代码中,这个值将通过访问Request.UrlReferer来获得。这就使您的Action方法与HttpContext产生了依赖,因此它的单元测试代码就需要这样编写:

[TestMethod]
public void DeleteTest()
{
    string userName = "jeffz";
    Uri urlReferrer = new Uri("http://www.microsoft.com");

    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Setup(c => c.Request.UrlReferrer).Returns(urlReferrer);

    var mockController = this.GetMockController();
    mockController.Setup(c => c.MiddleTier.UserManager.Delete(userName)).Verifiable();
    mockController.Object.ControllerContext = new ControllerContext(
        mockHttpContext.Object, new RouteData(), mockController.Object);

    mockController.Object.Delete(userName)...
}

在单元测试代码中,我们Mock了一个HttpContextBase对象,让它的Request.UrlReferrer属性返回我们准备好的对象,再构造一个新的ControllerContext并交给Controller。而如果我们的UrlReferrer能够作为Delete方法的参数,那么单元测试代码就会一下子简单很多:

[TestMethod()]
public void DeleteTest()
{
    string userName = "jeffz";
    Uri urlReferrer = new Uri("http://www.microsoft.com");

    var mockController = this.GetMockController();
    mockController.Setup(c => c.MiddleTier.UserManager.Delete(userName)).Verifiable();

    mockController.Object.Delete(userName, urlReferrer)...
}

有些朋友可能会问,不就是从Request的UrlReferrer属性中取值吗?我们为什么要构造一个ControllerContext,不能直接设置Controller对象吗?例如这样就简单多了:

mockController.Setup(c => c.Request.UrlReferrer).Returns(urlReferrer);

似乎可行,不过您运行的时候就会发现,框架会抛出异常,说只有接口的成员,或可以override的成员才能够被Mock。没错,Controller的Request属性不是virtual的,无法override。Controller类如此设计是故意的,目的就是限制了可用的路径。试想,如果您Mock了Controller.Request属性,但是程序代码通过Controller.HttpContext.Request进行访问又怎么办呢?类似的做法还有对方法重载的设计。一般来说,都会把其中几个方法委托给其中唯一的方法,而只有那个方法是可以被override的。这样在编写测试时,我们仅有的Mock入口便确定了,避免了测试代码过度了解方法实现的问题。

回到正题。如果要让Delete方法接urlReferrer受参数,那么我们就要编写Model Binder相关的组件:

public class UrlReferrerModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        return controllerContext.HttpContext.Request.UrlReferrer;
    }
}

并使其可以直接运用到Action的参数上:

public class UrlReferrerAttribute : CustomModelBinderAttribute
{
    private static UrlReferrerModelBinder s_modelBinder =
        new UrlReferrerModelBinder();

    public override IModelBinder GetBinder()
    {
        return s_modelBinder;
    }
}

于是乎,我们的Delete方法便可写为:

public ActionResult Delete(string userName, Uri urlReferrer)
{
    this.MiddleTier.UserManager.Delete(userName);
    return this.Redirect(urlReferrer.ToString());
}

如今的代码,无论是应用程序还是框架类库,都必须考虑“可测试性”这个要求。例如.NET 3.0的WF,由于其可测试性不佳一直为人所诟病。现在我们在编写程序时,要时刻询问自己:“这么做方便测试吗?”考虑到这个问题,可能您就会放心地做出某些抉择了2

 

注1:其实还是可以Mock的。例如Typemock使用Profiler的方式进行直接注入,可以Mock任何成员。不过,如果Moq等框架无法满足您的需要,一般便是您的设计有些问题了。

注2:例如,究竟让Action方法返回ActionResult,还是返回void,并直接通过Response输出呢?

1
0
(请您对文章做出评价)
« 上一篇:请别埋没了URL Routing
» 下一篇:iReaper:WebCast视频下载辅助工具
Add your comment

64 条回复

  1. #1楼 glonely      2009-03-09 00:49
    抢个沙发,睡觉
      回复  引用  查看    
  2. #2楼 glonely      2009-03-09 00:50
    头一回坐赵老师的沙发,哈哈.
    明天再仔细看.. :)
      回复  引用  查看    
  3. #3楼 拼命占便宜      2009-03-09 00:51
    好吧.
      回复  引用  查看    
  4. #4楼 海洋——海纳百川,有容乃大.      2009-03-09 00:51
    看看,明天继续看,今天太晚了,得好好休息了。
      回复  引用  查看    
  5. #5楼 小手冰冰凉      2009-03-09 08:10
    来晚了。。。呵呵。。
      回复  引用  查看    
  6. #6楼 第一控制.NET      2009-03-09 09:06
    不错。眼前一亮。以前我都是封死到其他地方去。
      回复  引用  查看    
  7. #7楼 代震军      2009-03-09 09:12
    问题的确如此,我也考虑过这个问题,先收藏了:)
      回复  引用  查看    
  8. #8楼 你好~~[未注册用户]2009-03-09 09:28
    问老赵一个问题。
    您现在的项目(wodeyichu)是用的asp.net 的mvc框架吗?
    因为我们想在正式的项目中使用这个mvc框架,不知道该框架是否成熟。
      回复  引用    
  9. #9楼 Modo[未注册用户]2009-03-09 09:31
    mvc 碰都没碰
      回复  引用    
  10. #10楼[楼主] Jeffrey Zhao      2009-03-09 09:31
    @你好~~
    1、不是,因为MVC框架出来的晚。
    2、成熟,因为其他领域已经存在很久了。
      回复  引用  查看    
  11. #11楼[楼主] Jeffrey Zhao      2009-03-09 09:31
    --引用--------------------------------------------------
    Modo: mvc 碰都没碰
    --------------------------------------------------------
    真可惜
      回复  引用  查看    
  12. #12楼 binglingshui      2009-03-09 09:37
    回家接着看。
    上班有点忙!
      回复  引用  查看    
  13. #13楼 海洋——海纳百川,有容乃大.      2009-03-09 09:48
    老赵,最近有没有研究 dynamicdata 技术? http://www.asp.net/dynamicdata/
      回复  引用  查看    
  14. #14楼 Q.Lee.lulu      2009-03-09 09:51
    快枪赵最近发文就是快
      回复  引用  查看    
  15. #15楼[楼主] Jeffrey Zhao      2009-03-09 10:02
    @海洋——海纳百川,有容乃大.
    没有,计划等到正式版出来再说
      回复  引用  查看    
  16. #16楼 对付[未注册用户]2009-03-09 10:13
    请写些负载均衡 容错平衡 主机集群 数据库集群 应用服务器集群的主题,不要只讲那些开发应用的小技巧
      回复  引用    
  17. #17楼 Nick Wang      2009-03-09 10:22
    如今的代码,无论是应用程序还是框架类库,都必须考虑“可测试性”这个要求
    --------------------------------------------------------------------------
    如果你一开始就是用TDD开发的,基本上能保证“可测试性”。
      回复  引用  查看    
  18. #18楼[楼主] Jeffrey Zhao      2009-03-09 10:34
    --引用--------------------------------------------------
    对付: 请写些负载均衡 容错平衡 主机集群 数据库集群 应用服务器集群的主题,不要只讲那些开发应用的小技巧
    --------------------------------------------------------
    这些不是“小技巧”,是编程实践。
    你说的这些没有太大价值,我不是说这方面没有技巧或用途不大,但是我可以说我见过太多在这方面说白了就是堆砌现有产品。这方面靠说的话基本上都是纸上谈兵,例如您现在这样的名词列举,呵呵。
    要我吹嘘我也可以吹得天花乱坠,产品和技术列举的piapia的,可惜价值不大。反倒是掌握思维,受益无穷。
    所以我只会在小圈子里谈这些,欢迎加我msn单聊或交流心得之类的,呵呵。
      回复  引用  查看    
  19. #19楼[楼主] Jeffrey Zhao      2009-03-09 10:36
    @对付
    好吧,其实这个理由是更重要的:我在这方面只有心得,没有创新,我写的出来的东西互联网上到处可以找到,因此拉不下脸写这些东西,呵呵。
      回复  引用  查看    
  20. #20楼 风海迷沙      2009-03-09 11:14
    在ASHX中当然使用Context输出了,在ASPX中当然使用RESPONSE输出。
    测试上很不一样么?
      回复  引用  查看    
  21. #21楼 rainnoless      2009-03-09 11:18
    看着老赵的文章,我发现自己离服务器端越来越远了,呵呵。
    路过,没有看懂,因为好久没有接触过VS2003/.NET1.1以外的世界,呵呵。真相配置一套VS2010+.NET4.0体验一下感觉,呵呵。然后再学习老赵的学习精神,回顾到.NET3.5和C#3.0中来,呵呵。 不过暂时都是在梦中游荡,呵呵。
      回复  引用  查看    
  22. #22楼[楼主] Jeffrey Zhao      2009-03-09 11:18
    --引用--------------------------------------------------
    风海迷沙: 在ASHX中当然使用Context输出了,在ASPX中当然使用RESPONSE输出。
    测试上很不一样么?
    --------------------------------------------------------
    没听懂你的意思。
      回复  引用  查看    
  23. #23楼[楼主] Jeffrey Zhao      2009-03-09 11:18
    --引用--------------------------------------------------
    rainnoless: 看着老赵的文章,我发现自己离服务器端越来越远了,呵呵。
    路过,没有看懂,因为好久没有接触过VS2003/.NET1.1以外的世界,呵呵。真相配置一套VS2010+.NET4.0体验一下感觉,呵呵。然后再学习老赵的学习精神,回顾到.NET3.5和C#3.0中来,呵呵。
    --------------------------------------------------------
    客户端也是一条光明大道阿
      回复  引用  查看    
  24. #24楼 韦恩卑鄙      2009-03-09 12:17
    老赵 我来吐槽
    上次听了你的mvc讲座 我更加坚定了我的silverlight2 方向上的决心 亩哈哈
    mvc不是不好 就是不适合我这样的懒人
    我们项目组就在维护修改一个 asp+vb6 的mvc框架产品
    每天吐血的心都有啊!
      回复  引用  查看    
  25. #25楼[楼主] Jeffrey Zhao      2009-03-09 12:25
    @韦恩卑鄙
    那么支持一下,别来和我抢生意,哈哈
      回复  引用  查看    
  26. #26楼 韦恩卑鄙      2009-03-09 12:51
    恩 等着坐享老赵你的成果了 支持

      回复  引用  查看    
  27. #27楼 水果阿生      2009-03-09 13:35
    @对付
    同学你比我幽默多了。早上吃的什么?
      回复  引用  查看    
  28. #28楼 重典      2009-03-09 14:40
    我现在是将所有涉及到HttpContext的Action都分离为单独的类

    这些类又抽像出接口,测试时是对这些接口建立新的Mock类

    这样实现了MVC的可测试性,但是类越来越多

    但是由于部分具体实现的类,如用户信息与HttpCOntext也存在直接依赖关系,而且也有的存在很多逻辑性的代码,需要测试,那我是否也要将这里再次解耦

    也就是说逻辑与僵化的代码相解耦,仅对逻辑进行测试
      回复  引用  查看    
  29. #29楼 重典      2009-03-09 14:44
    @韦恩卑鄙
    我也在维护这么个东西。。。。
    吐胆汁的心都有
      回复  引用  查看    
  30. #30楼[楼主] Jeffrey Zhao      2009-03-09 14:49
    @重典
    如果有业务要依赖HttpContext,那么总归有东西要依赖HttpContext,只是使用一定抽象把HttpContext封装起来,再使用数据与逻辑进行交互。总之还是隔离,把数据的提取逻辑,和数据的使用逻辑隔离开来。这样两种逻辑可以分别地进行测试。
    我觉得你的做法应该也是可取的,具体使用什么方式,视情况而定,不妨举个简单的例子?
      回复  引用  查看    
  31. #31楼 神之恩宠      2009-03-09 15:27
    老赵,怎么不和我们把Oxite分析分析呀。。。。
      回复  引用  查看    
  32. #32楼[楼主] Jeffrey Zhao      2009-03-09 15:50
    --引用--------------------------------------------------
    神之恩宠: 老赵,怎么不和我们把Oxite分析分析呀。。。。
    --------------------------------------------------------
    让它再发展发展吧,有机会再讨论。
      回复  引用  查看    
  33. #33楼 廉毅      2009-03-09 17:19
    你好啊请问能不能给微软mvc开发组个建议,在生成VIEW时可以选择用列说明(列注释)来生成列标题呢?因为只能用列的字段名生成列标题可能适合英语国家,但对于用中文的很不方便,生成后还要手工再改
      回复  引用  查看    
  34. #34楼[楼主] Jeffrey Zhao      2009-03-09 17:23
    @廉毅
    这点修改很正常吧,本来就没有说直接用的。
    如果用注释的话,注释的格式呢?是不是又要追求灵活性。
    于其如此,不如自己写一个吧。
      回复  引用  查看    
  35. #35楼 小洋      2009-03-09 20:14
    学习;)
      回复  引用  查看    
  36. #36楼 你好~~[未注册用户]2009-03-10 09:23
    老赵,请教一个问题。
    在mvc中,Controller是通过给方法加参数来接收url的参数。
    我现在遇到一个问题是我想在用户控件或是母模板中接收url参数。Request是接收不到。有什么方法啊?
      回复  引用    
  37. #37楼[楼主] Jeffrey Zhao      2009-03-10 09:45
    @你好~~
    View只应该从ViewData中获取数据。
      回复  引用  查看    
  38. #38楼 你好~~[未注册用户]2009-03-10 10:15
    场景是这样的。
    多个页面都要显示某个用户的10个好友,我希望使用用户控件获取受访用户的id显示他的好友信息。
    您有什么建议。谢谢!
      回复  引用    
  39. #39楼[楼主] Jeffrey Zhao      2009-03-10 10:17
    @你好~~
    asp.net mvc没有用户控件的概念。
    你需要在那些页面的Model中准备一组数据(例如n各用户表示好友),然后把这组数据交给Partial View来呈现。
      回复  引用  查看    
  40. #40楼 廉毅      2009-03-12 09:19
    没见过mvc做的企业应用的案例,见到的以blog之类的东西居多,企业应用价值开发不大,希望提供个mvc企业成功构架的实际案例,比如进销存、物流等,真怕mvc到最后也就是做做博客论坛之类的东西。。。
      回复  引用  查看    
  41. #41楼[楼主] Jeffrey Zhao      2009-03-12 09:33
    @廉毅
    1、这是表现层技术,表现层技术与实际平台能力其实关系不大。
    2、MVC这种驱动方式,早在其他平台就被充分验证了。
      回复  引用  查看    
  42. #42楼 撒旦之吻      2009-03-17 16:52
    在Webfrom当中本人对HttpContext依赖确实是重。。

    就像Community Server中都要把这个上下文包装。感觉用习惯,至于MVC当中的话,没有仔细看过,但是机制肯定是改变了。。。呵呵。。。待老赵分析。。
      回复  引用  查看    
  43. #43楼 的发生地方[未注册用户]2009-03-18 15:40
    457457
      回复  引用    
  44. #44楼 Zealic      2009-04-02 16:28
    @JefferyZhao

    ControllerContext 的 Request 属性可以被 set,因此这里的论断:Request 无法 Mock 是不正确的。

    我们可以在测试开始之前(如 SetUp),或 TestMethod 的 Prepare 中实例化一个 RequestContext,它的构造器参数中的对象都是可以 Mock 的。

    顺带提一下,Moq 确实是好东西,我以为用的人很少,没想到老赵居然也用。
      回复  引用  查看    
  45. #45楼[楼主] Jeffrey Zhao      2009-04-02 16:39
    @Zealic
    ControllerContext的Request属性?有这个属性吗?
    // moq使用已经很广泛了吧,它推广很迅猛的……
      回复  引用  查看    
  46. #47楼[楼主] Jeffrey Zhao      2009-04-02 17:46
    @Zealic
    我说的不就是这个意思吗?我说的是:没有办法直接Mock那个Request属性,而只能通过Mock那个HttpContext进行,这么做的原因是保证了Mock的单一入口。
    你一定要说,通过HttpContext属性进行Mock,也就相当于Request属性能够Mock,这个……不纠缠于这个概念,呵呵。
      回复  引用  查看    
  47. #48楼 Zealic      2009-04-02 17:50
    @Jeffrey Zhao
    -> 注一
    此外,我是反对使用 TypeMock 进行 Mock 的,虽然他的魔法很神奇(这东西还要钱)。
    原因是,在持续集成中,使用 NCover/PartCover 运行基于 TypeMock 的单元测试时,由于存在两个 Profiler ,会造成冲突。 导致无法运行结果与预期不相符。

    ->注二
    我觉得对于复杂结果使用 ActionResult,简单结果直接 Response。
      回复  引用  查看    
  48. #49楼 Zealic      2009-04-02 18:00
    @Jeffrey Zhao
    确实 Request 不能 Mock,但是既然它已经"固定"依赖于外部 HttpContext,之所以提供这个属性就是为了让 Controller 能快速访问而已,又何必让它可以 override 呢?
    我想这也许就是 MVC Team 的考虑吧 :)
    又或者,MVC Team 设计时根本就没有考虑视图的单元测试(这事相当的吃力不讨好,个人认为测试下 500 OK 就好)
    再说,单元测试本身就要求编写者了解被测试单元的逻辑,自然也包括所有依赖。
      回复  引用  查看    
  49. #50楼[楼主] Jeffrey Zhao      2009-04-02 18:08
    @Zealic
    你是不是没有看清我的文章……我说的完全就是你说的……
    每次测试的是Controller的一部分,为了测这一部分,会Mock另外一部分。
      回复  引用  查看    
  50. #51楼 Zealic      2009-04-02 19:35
    @Jeffrey Zhao
    现在刚回家,仔细看了一遍

    是我钻牛角尖了

    就当刚才的讨论是演示代码膨胀和解决依赖的繁琐吧。

    此外还想问下,老赵是怎么解决单元测试中准备测试数据/模型的问题的?

    类似 ROR 那样用 xml 饭序列化对象、直接用代码构造或其他?

      回复  引用  查看    
  51. #52楼[楼主] Jeffrey Zhao      2009-04-02 20:21
    @Zealic
    单元测试的话,数据一般都是硬编码的吧,还是我没有理解你的意思呢?
      回复  引用  查看    
  52. #53楼 Nick Wang      2009-04-02 21:30
    现在的测试框架都支持csv数据源吧
    新版的mbunit好像还支持xml得
      回复  引用  查看    
  53. #54楼[楼主] Jeffrey Zhao      2009-04-02 23:29
    @Nick Wang
    支不支持一回事,需不需要是另一回事啊,而且我觉得我们在说的不是一个东西。兄弟你介绍一下?
      回复  引用  查看    
  54. #55楼 Nick Wang      2009-04-02 23:42
    @Jeffrey Zhao
    到底说的是啥啊,我有点糊涂了。你想让我介绍点什么?
      回复  引用  查看    
  55. #56楼[楼主] Jeffrey Zhao      2009-04-03 08:23
    @Nick Wang
    介绍一下单元测试中对于测试数据的数据源的处理?
      回复  引用  查看    
  56. #57楼 Zealic      2009-04-03 10:20
    准备测试数据是指包括准备数据库、数据连接、实体文件等一系列测试所必须的数据和外部依赖状态。

    现在大多情况都是直接用代码来写,这样很不好。
    理想的情况是,像 DbUnit 那样可以通过配置直接准备好需要的数据和外部依赖状态。目的就是,减少单元测试准备数据\状态代码的量。

    ROR 的 YAML Fixture 这一点上做的很好。

    像 MbUnit 虽然也有一些这样的功能(Attribute + XML),但首先要依赖于 MbUnit 这个框架,功能也不是太完善,所以依然不是我想要的;如果没有这方面的东西,也许我得自己写一个。
      回复  引用  查看    
  57. #58楼 Nick Wang      2009-04-03 11:09
    @Zealic
    为什么要用数据库呢?里面有什么?
    单元测试如果需要连接数据库就不是单元测试了。你可以将访问数据库的代码单独提取出来做成一个类,然后提取接口,这样要测试的类就会依赖接口了,然后就可以用mock来准备数据了
      回复  引用  查看    
  58. #59楼 Nick Wang      2009-04-03 11:14
    也可以用一个fake的数据库实现类来准备数据,在内存中准备数据,速度没有问题。 如果你是想测数据库搜索功能什么的,那就是数据库的单元测试了,应该测SP而不是.NET代码。
      回复  引用  查看    
  59. #60楼 Zealic      2009-04-03 17:18
    @Nick Wang
    之所以要准备数据库,是因为要测试 DataAccess 的实现。否则就不能达到测试 生成/编写的 sql 语句的目的。你所说的 fake 数据库,大概以为我要测试的是逻辑层吧。

    你可以了解以下 DbUnit(Java, http://www.dbunit.org/),是一个很成熟的数据库单元测试辅助工具。
      回复  引用  查看    
  60. #61楼 Nick Wang      2009-04-03 17:44
    我觉得从头到尾我都有点晕,不知道到底在讨论什么。你的问题到底是啥啊?看起来好像你都懂,没啥问题啊。
      回复  引用  查看    
  61. #62楼 rad      2010-01-11 19:02
    弱弱的问下,最后一个代码里面
    public ActionResult Delete(string userName, Uri urlReferrer)

    是不是应该为
    public ActionResult Delete(string userName, [UrlReferrer]Uri urlReferrer)
    ?,我只有这样才可以...
    另外我这样做了以后,如果我在其他地方调用这个action的时候,例如
    <% html.ActionLink<HomeController>(c=>c.Delete(1),"Delete")%>
    是不是一定要写全两个参数啊 c=>c.Delete(1, null)这样?
    如何做到只传一个参数就可以呢?
      回复  引用  查看    
  62. #63楼[楼主] Jeffrey Zhao      2010-01-13 12:14
    @rad
    http://www.cnblogs.com/JeffreyZhao/archive/2009/12/27/valuable-posts-index.html#url-gen-by-route
      回复  引用  查看    
  63. #64楼 rad      2010-01-13 16:35
    @Jeffrey Zhao
    没有没有,可能我没有说明白我的意思
    我的意思是
    如果有这么一个Action
    public ActionResult Delete(string userName, Uri urlReferrer)

    那么我在其他地方掉转到这个Action的时候,是不是一定要指定2个参数(userName, urlReferrer)呢?但是这里的url已经获取了呀,所以我想可不可以只指定一个参数?
      回复  引用  查看    
  64. #65楼[楼主] Jeffrey Zhao      2010-01-13 16:50
    @rad
    你说明白了,我也理解了。
      回复  引用  查看