Wednesday, 16 December 2009

ASP.NET MVC Supporting Multiple Actions Through a Single Form

One of the things that always plagues me in ASP.NET MVC is supporting multiple actions through a single form.  Sometimes this is not a good idea, but often we need to do it to support multiple functions on the same data.  I’ve tried using javascript to override the form action but it takes a lot of complicated code and is much harder when using ajax calls. I’ve tried calling a single action and then splitting out the data to call the sub methods but unless you use redirections it makes your methods much harder to test and you lose your form if you redirect, plus the code is messy.

Finally I can across this post by MAARTEN BALLIAUW.  It specifies that you can use a custom attribute to determine which action is valid depending on other parameters.  It’s very smart and I highly suggest you read it, in fact I’m going to assume at this point that you have.

I took it a little bit further and have some hints for you.  You’ll likely get an error first time you try it saying that there is ambiguity between the actions, be sure that your form is calling an action that does not exist.  This code is going to decide if an action is valid, if the Index action is valid and the form post variable also says your post action is valid then it’s going to throw this exception.  An action that doesn’t exist can’t be valid, so only the one related to your form post variable will be valid.

Also, I don’t like to use the value of the submit button.  The value is also what the user sees and is likely to be changed by the business.  For this reason all I want to do is verify that the form variable was submitted.

I’ve changed the code in the attribute to read:

   1: public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
   2: {
   3:    //return controllerContext.HttpContext.Request[MatchFormKey] != null &&
   4:    //    controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
   5:    // Commented out because we don't want to match the value, just the key.
   6:  
   7:    if (MatchFormValue == null)
   8:    {
   9:        return controllerContext.HttpContext.Request[MatchFormKey] != null;
  10:    }
  11:    else
  12:    {
  13:        return controllerContext.HttpContext.Request[MatchFormKey] != null &&
  14:            controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
  15:    }
  16: }

It’s a fantastic solution so far, my thanks to Maarten.

Thursday, 10 December 2009

ASP.NET MVC 2 Areas in a Converted ASP.NET Application

I recently converted our ASP.NET Application to ASP.NET MVC 2.  This allows me to run ASP.NET pages along side ASP.NET MVC pages without any trouble.  Well we have a big new functionality project coming up and it would be a shame not to take advantage of the ASP.NET MVC 2 areas.  The areas  tutorial on MSDN is actually extremely good showing all you need to know on how to setup areas, but it won’t show you how to set it up in a converted ASP.NET web app. 

It’s not too much different really, just a small change.  In the step “Enabling the Custom Build Step for MVC Areas Projects” you’re asked to un-commend some information from the csproj files.  The ASP.NET application does not have this  section.  Fortunately you can jsut copy and paste it in after the already commented out “AfterBuild” section.  It’s pasted below, ready for the master web app.

   1: <!-- To enable MVC area subproject support, uncomment the following two lines: -->
   2:   <UsingTask TaskName="Microsoft.Web.Mvc.Build.CreateAreaManifest" AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   3:   <UsingTask TaskName="Microsoft.Web.Mvc.Build.CopyAreaManifests" AssemblyName="Microsoft.Web.Mvc.Build, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   4:  
   5:   <Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
   6:     <PropertyGroup>
   7:       <AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
   8:     </PropertyGroup>
   9:     <!-- If this is an area child project, uncomment the following line:
  10:     <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
  11:     -->
  12:     <!-- If this is an area parent project, uncomment the following lines: -->
  13:     <CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
  14:     <CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
  15:  
  16:   </Target>
  17:   <Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
  18:     <AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
  19:   </Target>

Take all of it, you’ll need it.  Once you’ve done that and reloaded the site (be sure to set your startup project again) you’re good to go.  Also bear in mind that your project should have the same name as the area path, if you want http://localhost/MyNewArea/Controller/Action/id then you should call your project MyNewArea not MyNamespace.MyNewArea.  When you build the views are placed into the Areas folder of the parent project, they are placed under the folder which is the namespace of the project.  There could be a way around this, let me know if you have one, but it’s pretty easy to keep the namespace simple.

Tuesday, 8 December 2009

Continuous Integration – Why use MSBuild?

I’ve always loved continuous integration.  Usually I use CruiseControl.net for my .NET applications, and really it doesn’t matter which environment you use as long as it supports MSBuild.  But why should you use MSBuild?  There are many alternatives out there, NAnt being the most popular, but they don’t get a lot of support, certainly not as much as Microsoft puts behind MSBuild.  Microsoft is not known for inventing the wheel, they quite publicly take good ideas and do their best to make them better. 

The style of MSBuild is very similar to NAnt, an XML based build script.  Project files in visual studio are now written in MSBuild script.  You can edit them by hand, add pre and post build events if you want to.  The script is very straight forward, if you’ve used Ant before then you’ll be familiar with it.  Create targets, dependencies, run executables and build project files.  Support for building projects and solutions is built in, file system interface is powerful and easy to use.

Item Groups are great, if you want to process a bunch of files but disregard certain file types you can setup an item group that defines the rule.

   1: <ItemGroup>
   2:     <!--This group of files will be published as part of the website.-->
   3:     <MySourceFiles Include="..\WebProject\**\*" 
   4:                    Exclude="..\WebProject\**\.svn\**;..\HotelsCombined.WebUI\**\*.pdb"/>
   5:   </ItemGroup>
The above example shows how to define an item group for a web project that takes all files from the project except the subversion source and the pdb files.  Useful if you want to deploy a site.
   1: <Target Name="PublishDevelopment" DependsOnTargets="Compile">
   2:   <Delete Files="Publish\**\*"/>
   3:   <Copy SourceFiles="@(MySourceFiles)" 
   4:         SkipUnchangedFiles="true"
   5:         DestinationFiles="@(MySourceFiles->'Publish\%(RecursiveDir)%(Filename)%(Extension)')"/>
   6:   <Copy SourceFiles="development.web.config" DestinationFiles="Publish\web.config"/>
   7: </Target>
The above example will take the MySourceFiles item group and copy it to my output directory, then take my development.web.config and replace the web.config.

Properties passed in get pushed up the tree, so if you pass /p:Configuration=Release into the script it will push it up to any build files that you call.

Why use it over another tool such as NAnt?  Well NAnt can do most things that MSBuild can do really and if you do use NAnt you’ll get a lot of community support.  Both are supported by the popular continuous integration environments.

The Clincher

For me it boils down to two things, Microsoft is heavily supporting MSBuild and support is really important to me.  New features will be added and Microsoft will continue to advance the product as they are very heavily invested in it. NAnt has been in beta since 2007.  If I want to be sure that .NET 4.0 will be supported I’ll go with MSBuild.  The other feature is IDE integration.  MSBuild is supported in Visual Studio out of the box, no extensions necessary.  It’s not as important as the support issue, but it’s nice to have.

Thursday, 26 November 2009

My Podcasting List

This is a list of the current podcasts I listen to.  I usually listen to these at work, though occasionally on the way to/from work.

Development Technical Podcasts

.Net Rocks iTunes link This show covers a great range of .NET topics and is really well presented.  I’m not sure how the guys who brought you Mondays are also able to bring you such a great technical podcast, but I’d recommend it to any .NET developer.
Hanselminutes iTunes Link Scott Hanselman talking about anything technical.  Most of the stuff is .NET and development related and Scott is a great presenter.
Stack Overflow Link Everyone’s favorite site now produces a podcast, it’s not quite as entertaining as the other podcasts, but it’s very informative and well presented.

Other Tech Podcasts

PING Link Channel 9’s ping is just good fun, I can’t find a good podcast location for downloading it to my iPhone through iTunes, but I watch it online anyway.
TWiT Link Leo La-port is a great podcaster and TWiT covers some great topics each week.  This seems to be one of those shows that some people just REALLY hate, but I’m not sure why.  Take it with a grain of salt, but it’s good fun, entertaining and informative (even if some times opinionated).
Aussie Geek Link I’m an Aussie, I’m a geek.  This is just pitched at me.  It’s not bad, not as good as TWiT but similar, focused more on Aussie news as well which is great.

Entertainment

Answer Me This Link These guys are so great, very funny and well presented podcast.  If you’re looking for a humorous podcast, this is worth a listen.  My favorite podcast!
Carpool Link - Awesome site! Robert Lewellyn is a very interesting entertainer who has created one of the most fantastic and informative podcasts with an innovative and brilliant format.  You’ll like this podcast more if you like cars and alternative energy, or even if you’re a fan of Red Dwarf.
Hardcore History Link Ever wanted to know more about the plague?  Or the Ancient Mogols?  Perhaps you want to know about the rise and fall of the Asirians?  This is a great place to learn about it.  Well presented and great tempo, this is a good fun podcast that I listen to between audio books.
College Humor iTunes Link Just funny, really really funny.

Monday, 9 November 2009

Testing function parameters with Rhino Mocks

When I’m developing, often I need to test the parameters that were passed to a function.  This is most often the case when dealing with the repository pattern.

For example, if I’m writing a processor that goes through a list of objects and inserts them into the database using a repository I would wish to know that my repository is called 10 times.  I might also with to ensure that it is called 10 times in the correct order and that each time the correct object is passed into the method.

There are a few ways to do this, one way is to keep the objects in memory in my test class and assert that the correct object was passed in:

   1: IRepository repo = mocks.DynamicMock<IRepository>();
   2: mocks.ReplayAll();
   3:  
   4: // testing code
   5:  
   6: repo.AssertWasCalled(repo.Insert(objects[0]));
   7: repo.AssertWasCalled(repo.Insert(objects[1]));
   8: repo.AssertWasCalled(repo.Insert(objects[2]));
   9: repo.AssertWasCalled(repo.Insert(objects[3]));
  10: repo.AssertWasCalled(repo.Insert(objects[4]));
  11: repo.AssertWasCalled(repo.Insert(objects[5]));
  12: repo.AssertWasCalled(repo.Insert(objects[6]));
  13: repo.AssertWasCalled(repo.Insert(objects[7]));
  14: repo.AssertWasCalled(repo.Insert(objects[8]));
  15: repo.AssertWasCalled(repo.Insert(objects[9]));

But this isn’t going to test the order they were called in.  Also, often I find that I don’t have a reference to the object that is being passed into the method even though I might know it’s contents.

One way to get around this is to expect that when the method is called to use a delegate and add the parameters to a list we can test later.

   1: IRepository repository = mocks.DynamicMock<IRepository>();
   2: List<MyObject> args = new List<MyObject>();
   3:  
   4: using (mocks.Record())
   5: {
   6:     Expect
   7:         .Call(delegate { repository.Insert(null) })
   8:         .IgnoreArguments().WhenCalled(o => args.Add(o.Arguments.First() as MyObject))
   9:         .Repeat.AtLeastOnce();
  10: }

Note the delegate, used because repository.Insert is a void method.

This way, every time the function is called the parameter is added to the list.  I can now check the order the function was called in and the contents of each parameter used each time it was called.

Tuesday, 20 October 2009

Testing Objects Without Interfaces Using an Interface Wrapper and a Partial Class

I've found that often I'm using a framework that doesn't lend itself to unit testing very well. In the past I've used wrappers, a class that contains an instance of the object that is not testable and inheriting an interface that I can mock, but this is a lot of work.

A colleague of mine recently came up with a great idea using partial classes. If you create a partial class of your class you wish to mock and have the partial class inherit from an interface then you will be able to mock the interface for testing. All you need to do is create your stubs of any methods you wish to use in your interface and you're away.

It's not quite as clean as if your original object instantiated it's own interface, but it's much cleaner than a wrapper class. I thought that it was ingenious, for some reason I never thought of it. Oddly enough since he thought of it I've been finding implementations of it everywhere. A couple of instances of the idea online and then when I was interviewing a guy the other day, he suggested the same thing as if it was just common sense.

It's really true that there is no such thing as a professional developer.

Thursday, 24 September 2009

The Guild Season 3 Ep 3

This show is great,

<br/><a href="http://video.msn.com/video.aspx?mkt=en-US&amp;vid=80a029bc-7a7a-4f6e-b63c-8c4e73975e20" target="_new" title="Season 3 - Episode 3: Player Down">Video: Season 3 - Episode 3: Player Down</a>

Migrating Classic ASP.NET to ASP.NET MVC 2 – Project

I always laugh when I see ASP.NET referenced as Classic ASP.NET after I was exposed to “Classic” asp for a little while last year.  I mean no insult to anyone, it’s still a very relevant and popular framework, but it makes it easier for me to differentiate it in my blog.

This post by Maarten Balliauw is old, but still very relevant on how to migrate ASP.NET to ASP.NET MVC.  It was put together during the beta of version 1 so there are a few things missing, but it does include 95% of what you need to do.  I don’t want to re-cover someone else’s work, so I’ll assume you’ve started there before you found this page.

Extra Bits

One thing the article leaves out is that you won’t have the ability to right click your Controllers folder to add a new controller.  While this doesn’t really stop you from adding controllers as they are just classes, it does cause you to spend extra time writing the wrapper information and leaves the possibility of error more open.  Fortunately this is easy to enable.  In the project file (aspmvcwebsite.csproj or whatever) you’ll find this line in bold:

   1: <PropertyGroup>
   2:     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
   3:     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
   4:     <ProductVersion>9.0.30729</ProductVersion>
   5:     <SchemaVersion>2.0</SchemaVersion>
   6:     <ProjectGuid>{9383F928-27DF-4A63-B30B-F79FF733B034}</ProjectGuid>
   7:     <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
   8:     <OutputType>Library</OutputType>
   9:     <AppDesignerFolder>Properties</AppDesignerFolder>
  10:     <RootNamespace>AdminWebUI</RootNamespace>
  11:     <AssemblyName>AdminWebUI</AssemblyName>
  12:     <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  13: </PropertyGroup>

It won’t, of course, be in bold in the csproj file, the ProjectTypeGuids define the flavour of project you chose when you started the project and it’s what the MVC framework looks for to setup your context sensitive menu.  All you need to do is add another flavour so your project type looks like an ASP.NET MVC project type.  If you create an ASP.NET MVC project and check out the csproj file you’ll see the extra guid you need, I’m not sure if it’s all the same for all flavours of MVC but mine was:

   1: {F85E285D-A4E0-4152-9332-AB1D724D3325};

Add this in first and you’ll have

   1: <ProjectTypeGuids>{F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Reload your project and you’ll have ASP.NET MVC context sensitive menus.  You can still add web forms and whatever else you like throughout your project.

View Web.config

If you’re going to have strongly typed views, very important for ASP.NET MVC, then you’ll also need the Web.config file in your Views folder.  I would suggest creating a new ASP.NET MVC project and then copying the file into the folder in your migration application.

Conclusion

That’s pretty much all you need.  You can run the two side by side with very little effort.  If you have any other issues, just compare the web.config files from a new ASP.NET MVC site with the config file that you already have and move the differences.  ASP.NET MVC 2 has this for example:

<dependentAssembly>
    <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
    <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>

That you’ll need when it gets to IIS, but is not needed locally. 

 

Hopefully someone finds this useful.