Scott Hanselman

ASP.NET MVC Session at Mix08, TDD and MvcMockHelpers

March 08, 2008 Comment on this post [38] Posted in ASP.NET | ASP.NET MVC | Mix | Programming | Source Code | Speaking
Sponsored By

NOTE: This post is based on an older preview version of ASP.NET MVC and details have very likely CHANGED. Go to http://www.asp.net/mvc or http://www.asp.net/forums for updated details and the latest version of ASP.NET MVC.

image All the sessions from Mix are up on http://sessions.visitmix.com/ for your viewing pleasure. I had a total blast giving the ASP.NET MVC Talk. The energy was really good and the crowd (of around 600, I hear) was really cool.

You can download the MVC talk in these formats:

I think the sound is a little quiet, so I had to turn it up some. It's better turned up a bit so you can hear the interaction with the crowd.

Here's some of the code from the talk you might be interested in. I'll post the rest very soon.

MvcMockHelpers

The first are the MVCMockHelpers used in the Test Driven Development part of the talk, and also in the 4th ASP.NET MVC Testing Video up at www.asp.net/mvc.

NOTE AND DISCLAIMER: This is just a little chunks of helper methods, and I happened to use Rhino Mocks, an Open Source Mocking Framework, the talk at Mix. At my last company I introduced TypeMock and we bought it and lately I've been digging on Moq also. I'm not qualified yet to have a dogmatic opinion about which one is better, because they all do similar things. Use the one that makes you happy. I hope to see folks (that's YOU Dear Reader) repost and rewrite these helpers (and better, more complete ones) using all the different mocking tools. Don't consider this to be any kind of "stamp of approval" for one mocking framework over another. Cool?

Anyway, here's the mocking stuff I used in the demo. This is similar to the stuff PhilHa did last year but slightly more complete. Still, this is just the beginning. We'll be hopefully releasing ASP.NET MVC bits on CodePlex maybe monthly. The goal is to release early and often. Eilon and the team have a lot more planned around testing, so remember, this is Preview 2 not Preview 18.

MvcMockHelpers - RhinoMocks

using System;
using System.Web;
using Rhino.Mocks;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;

namespace UnitTests
{
    public static class MvcMockHelpers
    {
        public static HttpContextBase FakeHttpContext(this MockRepository mocks)
        {
            HttpContextBase context = mocks.PartialMock<httpcontextbase>();
            HttpRequestBase request = mocks.PartialMock<httprequestbase>();
            HttpResponseBase response = mocks.PartialMock<httpresponsebase>();
            HttpSessionStateBase session = mocks.PartialMock<httpsessionstatebase>();
            HttpServerUtilityBase server = mocks.PartialMock<httpserverutilitybase>();

            SetupResult.For(context.Request).Return(request);
            SetupResult.For(context.Response).Return(response);
            SetupResult.For(context.Session).Return(session);
            SetupResult.For(context.Server).Return(server);

            mocks.Replay(context);
            return context;
        }

        public static HttpContextBase FakeHttpContext(this MockRepository mocks, string url)
        {
            HttpContextBase context = FakeHttpContext(mocks);
            context.Request.SetupRequestUrl(url);
            return context;
        }

        public static void SetFakeControllerContext(this MockRepository mocks, Controller controller)
        {
            var httpContext = mocks.FakeHttpContext();
            ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
            controller.ControllerContext = context;
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }

        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            SetupResult.For(request.HttpMethod).Return(httpMethod);
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

            SetupResult.For(request.QueryString).Return(GetQueryStringParameters(url));
            SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Return(GetUrlFileName(url));
            SetupResult.For(request.PathInfo).Return(string.Empty);
        }
       
    }
}

MvcMockHelpers - Moq

Here's the same thing in Moq. Muchas gracias, Kzu.

using System;
using System.Web;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;

namespace UnitTests
{
	public static class MvcMockHelpers
	{
		public static HttpContextBase FakeHttpContext()
		{
			var context = new Mock<httpcontextbase>();
			var request = new Mock<httprequestbase>();
			var response = new Mock<httpresponsebase>();
			var session = new Mock<httpsessionstatebase>();
			var server = new Mock<httpserverutilitybase>();

			context.Expect(ctx => ctx.Request).Returns(request.Object);
			context.Expect(ctx => ctx.Response).Returns(response.Object);
			context.Expect(ctx => ctx.Session).Returns(session.Object);
			context.Expect(ctx => ctx.Server).Returns(server.Object);

			return context.Object;
		}

		public static HttpContextBase FakeHttpContext(string url)
		{
			HttpContextBase context = FakeHttpContext();
			context.Request.SetupRequestUrl(url);
			return context;
		} 

		public static void SetFakeControllerContext(this Controller controller)
		{
			var httpContext = FakeHttpContext();
			ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
			controller.ControllerContext = context;
		}

		static string GetUrlFileName(string url)
		{
			if (url.Contains("?"))
				return url.Substring(0, url.IndexOf("?"));
			else
				return url;
		}

		static NameValueCollection GetQueryStringParameters(string url)
		{
			if (url.Contains("?"))
			{
				NameValueCollection parameters = new NameValueCollection();

				string[] parts = url.Split("?".ToCharArray());
				string[] keys = parts[1].Split("&".ToCharArray());

				foreach (string key in keys)
				{
					string[] part = key.Split("=".ToCharArray());
					parameters.Add(part[0], part[1]);
				}

				return parameters;
			}
			else
			{
				return null;
			}
		}

		public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
		{
			Mock.Get(request)
				.Expect(req => req.HttpMethod)
				.Returns(httpMethod);
		}

		public static void SetupRequestUrl(this HttpRequestBase request, string url)
		{
			if (url == null)
				throw new ArgumentNullException("url");

			if (!url.StartsWith("~/"))
				throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

			var mock = Mock.Get(request);

			mock.Expect(req => req.QueryString)
				.Returns(GetQueryStringParameters(url));
			mock.Expect(req => req.AppRelativeCurrentExecutionFilePath)
				.Returns(GetUrlFileName(url));
			mock.Expect(req => req.PathInfo)
				.Returns(string.Empty);
		}
	}
}

Maybe RoyO will do the same thing in TypeMock in the next few hours and I'll copy/paste it here. ;)

MvcMockHelpers - TypeMock

Thanks to Roy at TypeMock.

using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using TypeMock;

namespace Typemock.Mvc
{
    static class MvcMockHelpers
    {
        public static void SetFakeContextOn(Controller controller)
        {
            HttpContextBase context = MvcMockHelpers.FakeHttpContext();
            controller.ControllerContext = new ControllerContext(new RequestContext(context, new RouteData()), controller);
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.HttpMethod, httpMethod);
            }
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");
            var parameters = GetQueryStringParameters(url);
            var fileName = GetUrlFileName(url);
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.QueryString, parameters);
                r.ExpectAndReturn(request.AppRelativeCurrentExecutionFilePath, fileName);
                r.ExpectAndReturn(request.PathInfo, string.Empty);
            }
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }
        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }
        public static HttpContextBase FakeHttpContext(string url)
        {
            HttpContextBase context = FakeHttpContext();
            context.Request.SetupRequestUrl(url);
            return context;
        }
        public static HttpContextBase FakeHttpContext()
        {
            HttpContextBase context = MockManager.MockObject<HttpContextBase>().Object;
            HttpRequestBase request = MockManager.MockObject<HttpRequestBase>().Object;
            HttpResponseBase response = MockManager.MockObject<HttpResponseBase>().Object;
            HttpSessionStateBase sessionState = MockManager.MockObject<HttpSessionStateBase>().Object;
            HttpServerUtilityBase serverUtility = MockManager.MockObject<HttpServerUtilityBase>().Object;
            using (var r = new RecordExpectations())
            {
                r.DefaultBehavior.RepeatAlways();
                r.ExpectAndReturn(context.Response, response);
                r.ExpectAndReturn(context.Request, request);
                r.ExpectAndReturn(context.Session, sessionState);
                r.ExpectAndReturn(context.Server, serverUtility);
            }
            return context;
        }

    }
}
Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
March 08, 2008 8:37
Hey Scott, thanks for pasting the Moq version :) The escaping of the generics is kind of messed up (again). = i.e. var context = new Mock(); var request = new Mock(); var response = new Mock(); var session = new Mock(); var server = new Mock(); is missing all the on all mocks. :(. Do you use VS Paste (http://gallery.live.com/liveItemDetail.aspx?li=d8835a5e-28da-4242-82eb-e1a006b083b9&l=8) from WLW? I've never again had a problem with pasting code since I started using it...
March 08, 2008 8:50
When we can expect to those MVC HOL's be posted on web to download?
March 08, 2008 11:13
Hi, Any chance of Blogging about Asp.Net MVC with any one of dynamic languages in near future. Its high time, since there are no discussions around. It seems Dynamic languages were totally neglected in this MIX 2008, and SilverLight took away all the attraction. Since the DLR bits are around... may be you can start this now. Thanks IronRuby
March 08, 2008 11:51
Hi, In Ruby on Rails I can let the action respond to formats (like html (http request), js (ajax), xml and so on) and then render the view for the requested format. Can I do the same thing i ASP.NET MVC today, if not, come this funcionality be added later? Thanks Johan
March 08, 2008 14:57
It was a great presentation. There's one thing I'm still unclear on, though. Is MVC replacing Web Forms? :P
March 08, 2008 16:35
I think SIlverlight is quite good for video and screencasts, but wow, http://sessions.visitmix.com has some useless Silverlight to be honest. Somebody tell them about HTML please!
March 08, 2008 21:34
The Typemock version of the MvcMockHelpers class can be found here: Link: Typemock MVC Mock Helpers Roy.
March 08, 2008 23:40
Scott, Just watched your MIX presentation. I have to say that it was one of the most entertaining I've seen in a long time.
March 09, 2008 11:08
Hey Scott. I've watched your presentation and I just wanted to tell you it was great. Very easy-going, even for a not-native English speaker like me. Real edutainment, which is not always easy in a >1hour talk. It was great to see you referencing Linux, alt.net and some of the open source tools out there, the push on TDD and CI... and the jokes on ScottGu, too. xD
March 09, 2008 13:46
It was a great presentation. There's one thing I'm still unclear on, though. Is MVC replacing Web Forms?
NO, absolutely not. It's just ANOTHER OPTION to build ASP.NET apps, another web project for you to choose from. WebForms will continue to be fully supported. I think it was clear from the presentation. btw I like your humour Scott, way to do it. Everyone could see you're foreigner, not from the Microsoft ;), oh wait, already 6 months there ;P
March 09, 2008 15:20
Scott, The MVC session at Mix was fantastic. You crammed in the MVC pattern, the new MVC framework, TDD, mocking, and an entertaining delivery that was unmatched in any other session! Keep doing what you are doing! I also wanted to thank you for your graciousness in the scavenger hunt on Tuesday evening. You can read my run-down and see the photo we took on my blog post - Mix08 - Mixing it up with a Scavenger Hunt Thanks again!
March 09, 2008 16:31
One thing though... In method GetQueryStringParameters you should do Url decoding (HttpUtility.UrlDecode), shouldn't you?
March 09, 2008 16:32
I forgot to mention that I address ScottHa in the last comment.
March 09, 2008 18:49
Hi Scott, I just watched your MIX08 session. I have developed my research site by MVC framework since the first preview released. I got the problem when I use site master page. It was discussed by other guys in ASP.NET forum. URL: http://forums.asp.net/t/1195178.aspx It seems the issue still not be resolved at this new preview 2. I can only use the hacked solution in this thread. Could you take a look the issue?
March 09, 2008 20:21
Thanks Scott, this was excellent - informative and entertaining. You should give masterclasses in presentation!
March 09, 2008 20:51
Hi Scott, I took time to watch the presentation this morning. I found it very interesting. You guys seem to be taking it all very seriously, and I think you are doing a great job so far. Some of the points I have personally raised were addressed in the presentation - thanks. I think the greatest risk to getting developers to take this on will be what happens once you release version 1.0. Because most of us are inherently lazy, uptake will be slow in enterprise development - it is a lot of new technology to learn. Most MS enterprise developers I know don't even know HTML, which is why they use WebForms in the first place. (It's easy to learn if they know WinForms.) And don't even ask about CSS. NHaml is great too, but it will go over most people's heads. It seems like your target market are the web developers of the world - just like the audience for Ruby on Rails. Unfortunately it is going to be a long time before IIS7 (necessary for Routing to work properly, I assume) and SQL Server are as mature and cheap to run as Rails and MySQL. (And before you say "SQL Express, dude!", my hoster - webhost4life - just turned SQL Express off because of its poor performance.) If all that means that uptake of ASP.Net MVC is slow, I hope it means you don't give up. I think your decision to make Routes available to all web apps will be great for me. I will build it into my own apps. Can you use the Html.Action link thing from within WebForms too? But overall, I really like it. I'm looking forward to seeing the result - and then version 2. :-) Best regards, Richard
March 09, 2008 20:55
... oh, and I agree with "IronRuby" up above. Last year I was excited about the ASP.Net Futures, and Silverlight, because of the DLR. MIX07 was full of dynamic language stuff, and as far as I remember, IronRuby was one of your pet topics. Since then it's gone AWOL, and there is absolutely nothing appearing about it. I personally liked IronPython, but there's no support for it now - i.e. no tutorials, no help, no ASP.Net Futures, etc, etc. What's happened to it? Richard
March 09, 2008 22:13
I attended this session at Mix, and it was as entertaining as it was informatitve. Thanks, Rick
March 10, 2008 1:29
Hi Scott, The session was very nice and kept me glued all throughout the session. I think having a humourous tone in the talk was really nice. Might not be possible, but such tone should be used in screencasts too! that way learning is more fun!
March 10, 2008 9:58
Hi Scott, The sessions from MIX08 are absolutely great! A very interesting concept, in my opinion, is the combination of ASP.NET MVC and Silverlight 2.0. Can these two be hooked up together? Is it something you guys are thinking of, and is this something that you recommend? If so, it'd be great to maybe see a screencast on the subject, or a blog post that explains the basic concepts of working with MVC and Silverlight. Thanks!
March 10, 2008 12:41
Hi Scott Will we ever see ASP.NET MVC on .NET 2, or should I rather go with Spring.NET if I'm looking for an MVC framework for .NET 2. Thanks Horea
March 10, 2008 19:40
Hi Scott I can't work something out. Using your classes + Rhino how do I tell Rhino to expect a redirect? When I execute Controller.Response.Redirect("/Home/Index"); during record mode I get an error that the method is not implemented! Thanks Pete
March 10, 2008 20:42
Peter, You might want to use the "RedirectToAction()" method instead. In Scott's session he asked us to blog and post about our experiences, so I have begun a post at my blog to record my Wish List. So far the "Wish List" is a single short item: I would like to be able to use the UrlHelper Action method in a Static context. In my example, I tried to use it in my MasterPage definition but could not because I did not have a ViewPage instance. I'll add to my post above as I come across more items, otherwise, I'm just loving it! I'm sure I'll spend the rest of the month happily ensconced in MVC. Thanks, Joel Cochran
March 10, 2008 22:30
I am using RedirectToAction, but that uses Controller.HttpContext.Response.Redirect internally, which is why I am recording an expectation. To be quite honest I am finding anything other than the most basic test to be a *real* nightmare, it is putting me off even continuing to write tests for the website I am working on. My requirement is simple 01: Controller.Action(some parameters) 02: Ensure that ViewData["Message"] = "Some message" 03: Response.Redirect was executed to /Home/Index I wouldn't have thought this would have been difficult, and I don't think I am stupid either! Pete
March 10, 2008 22:38
Response.Redirect("/Home/Index", true) during playback works. Calling RedirectToAction("Index", "Home") doesn't trigger the Mock Response.Redirect(url) or MockResponse.Redirect(url, bool) In fact, none of these during recording works MockHttpContext.Response.Redirect("/Home/Index", false); MockHttpContext.Response.Redirect("Home/Index", false); MockHttpContext.Response.Redirect("/Home/Index", true); MockHttpContext.Response.Redirect("Home/Index", true); MockHttpContext.Response.Redirect("/Home/Index"); MockHttpContext.Response.Redirect("Home/Index"); It's silly!
March 11, 2008 7:26
Hi Scott, Just some small notes about style for the querystring parsing. First off, you can use the System.Uri class to help a bit (it'll give you in a more strongly-typed way whether there are querystring parameters), like so: string url = "http://www.mydomain.com/somefolder/getStuff.aspx?id=1&var2=abc&var3=55"; Uri uri = new Uri(url); then uri.Query will be "?id=1&var2=abc&var3=55", so you could do a string.IsNullOrEmpty on it. then, you don't need to use the 'ToCharArray' method on the string, instead just pass in a char type directly, using single quotes instead of double, as follows: string[] part = key.Split('='); these will make the code a tiny bit easier to read, as follows: string url = "http://www.mydomain.com/somefolder/getStuff.aspx?id=1&var2=abc&var3=55"; Uri uri = new Uri(url); NameValueCollection parameters = new NameValueCollection(); if (!string.IsNullOrEmpty(uri.Query)) { string[] keys = uri.Query.Substring(1).Split('&'); foreach (string key in keys) { string[] part = key.Split('='); parameters.Add(part[0], part[1]); } } finally, you can add all this as an extension method on the Uri class itself, as follows: public static class UriExtensions { public static NameValueCollection GetQueryStringCollection(this Uri uri) { NameValueCollection parameters = new NameValueCollection(); if (!string.IsNullOrEmpty(uri.Query)) { string[] keys = uri.Query.Substring(1).Split('&'); foreach (string key in keys) { string[] part = key.Split('='); parameters.Add(part[0], part[1]); } } return parameters; } } Two comments on this: I made the method return an empty collection instead of null, just made more sense to me at the time. Also, the Substring(1) is to get rid of the initial '&' character. Finally, the usage, which appears as follows: string url = "http://www.mydomain.com/somefolder/getStuff.aspx?id=1&var2=abc&var3=55"; Uri uri = new Uri(url); NameValueCollection parameters = uri.GetQueryStringCollection();
March 11, 2008 13:28
I just updated my wish list to add the following: I think we need an event or partial method that will execute inside the controller prior to the requested Action beginning. It would be very handy to execute Controller wide behavior at this level without needing every Action method to explicitly call another method. I imagine this could be used for logging, validation, security, and many other things. Joel
March 11, 2008 22:12
Scott, do you think it's possible to have for download the lab that were used at MIX? Cheers
March 12, 2008 5:31
Hi Scott, Thanks for the nice and very very helpful videos. Just wanted to ask , you , i culd have any .net samples on MVC that you showed in the videos. Regards
March 12, 2008 20:32
I updated my wish list again to add the following: Add an HtmlHelper method for a simple TableRow: this would be sure to assign the property names automatically to facilitate the use of UpdateFrom(). So far, I really like what I see. I'm trying to work my way through the rest of the videos and examples I can find. I have come across something, and I don't know if it is related to the project or not. I tried to use the Url.ActionLink() method inside a form action attribute and IntelliSense would not work. If I move the code out side the form to an independent code block it works just fine (confirming my references are OK). I tried it both with and without double quotes and no joy either way. It just seems strange to me. Anyway, thanks for everything. Joel
March 14, 2008 21:12
Scott, I've been going through the Advanced video, trying to figure out how to implement a site-wide error handler. This should be better than relying on the webserver or developing a custom 404 page. I've watched the section on InterceptController several times, which I think along with a *catchall Route could help me come up with a solution, but I have one burning question: how are HomeController and InterceptController related? I can see where InterceptController is handling whatever Controller is passed in, but I never can see how HomeController is made aware of InterceptController. How does this work? Did I miss something or is this more magic? Thanks, Joel
March 22, 2008 0:14
Joel Cochran wrote:
Add an HtmlHelper method for a simple TableRow: this would be sure to assign the property names automatically to facilitate the use of UpdateFrom().
Personally, I disagree with this approach, Joel. One of the big upsides to MVC, in my eyes, is that discrete control over the HTML is fairly broadly maintained without having to hack WYSIWYG code. It's a slippery-slope to create tags that are partial to the rest of the structure (i.e., must be within a table), because the next step is to just design the table... and, well, that sort of thinking is what makes such code bloat in the WebForms model. My 2 cents, -Matt
April 06, 2008 13:07
Very good stuff Scott, add me to your list of fans. I was having a problem mocking my context because I was saving to the database using Linq to sql and part of the save procedure, I check if the user is authenticated and also get the user's name. I add a couple more extension methods to create a Fake authenticated context, it basically mocks the User and identity objects in the context and returns true for IsAuthenticated... You can find the code on my blog right here I also have to say I like Moq best out of the other testing frameworks. Thanks and get us more videos :) -Emad
April 22, 2008 12:56
I tried your sample code with the new MVC refresh but the akeViewEngine.ViewContext was null, I guess the FakeViewEngine class needs to be rewritten to match the new RenderView method.... will try to do that... but woulden't mind having you show it to us as I am all new to mvc and tdd...
April 22, 2008 13:17
Puh, late day, I am getting tiered, the solution is: using (mocks.Playback()) { var result = controller.About() as RenderViewResult; Assert.AreEqual("About", result.ViewName); } instead of using (mocks.Playback()) { controller.About(); Assert.AreEqual("About", fakeViewEngine.ViewContext.ViewName); } the fake view engine does not need to be changed
April 22, 2008 13:32
PS, in the controller you will have to return RenderView("About"); with parameter for this to work.
May 05, 2008 6:57
hello, This is not an appropriate place to place my query, but need help, how can i add htmlAttributes like form id in Html.Form("CandidateAdd", "Candidate", FormMethod.Post, htmlAttributes) in mvc preview 2 previously it was like this Html.Form("CandidateAdd", "Candidate", new{id="frmcandidate"}) Regards, Taufiq Yusuf
May 05, 2008 7:43
i found a work around if anyone has better way to do it please: <%Dictionary HtmlAttributes = new Dictionary(); %> <% HtmlAttributes["id"] = "frm"; %> <% using (Html.Form("CandidateAdd", "Candidate", FormMethod.Post, HtmlAttributes))

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.