InvalidCastException after upgrading from ASP.NET MVC 3 to ASP.NET MVC 4

I have recently upgraded a MVC 3 application to MVC 4 but along the way en-counted some issues around upgrading references in the project.

Specially, I got this error.

1
2
3
4
5
6
7
8
9
System.InvalidCastException was unhandled by user code
Message=[A]System.Web.WebPages.Razor.Configuration.HostSection cannot be cast
to [B]System.Web.WebPages.Razor.Configuration.HostSection. Type A originates
from 'System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' in the context 'Default' at location
'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_1.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'.
Type B originates from 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'.
Source=System.Web.WebPages.Razor

Sources on the interweb say that you should change the webpages element back to 1.0.0.0 from 2.0.0.0 in your web config:

1
2
3
<appSettings>
  <add key="webpages:Version" value="1.0.0.0"/>
</appSettings>

but this seems a bit strange having to specify a old version to use the new version of System.Mvc.WebPages?

I believe the better solution is to add a binding redirect to tell any reference that has been using System.Mvc.WebPages 1.0.0.0 to use 2.0.0.0.

the following goes into your web.config

1
2
3
4
5
6
7
8
9
10
 <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
   
      <dependentAssembly>
          <assemblyIdentity name="System.Web.WebPages.Razor"
              publicKeyToken="31bf3856ad364e35" />
          <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

Upgrading MVC Project 2 to 4 and open it in VS 2012

I have an old MVC 2 project that works fine in VS 2010. However, if you try and open the project in VS 2012 you will get a “incompatible” error and left with no way to upgrade the project to VS 2012.

Here are some pointers to get it working (this is how I got it working for myself).

In VS2010, upgrade your MVC project to version 4. You need to follow the upgrade steps for upgrading to MVC 2 – 3 first.

http://www.asp.net/whitepapers/mvc3-release-notes#upgrading

Secondly, upgrade from MVC 3 to 4 using the following link.

http://www.asp.net/whitepapers/mvc4-release-notes#_Toc303253806

Once you have done this, you need to edit the project file of the working web application (i.e csproj). Firstly, open the solution in VS2012, the web project will not work but you can edit the project file directly in the IDE.

Make sure that you add the MVC 4 ProjectTypes GUID ({E3E379DF-F4C6-4180-9B81-6769533ABE47}) and remove the old MVC ProjectTypes GUID ({F85E285D-A4E0-4152-9332-AB1D724D3325}).

Here is a list of available GUIDS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Windows (C#) {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Windows (VB.NET) {F184B08F-C81C-45F6-A57F-5ABD9991F28F}
Windows (Visual C++) {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}
Web Application {349C5851-65DF-11DA-9384-00065B846F21}
Web Site {E24C65DC-7377-472B-9ABA-BC803B73C61A}
WCF {3D9AD99F-2412-4246-B90B-4EAA41C64699}
WPF {60DC8134-EBA5-43B8-BCC9-BB4BC16C2548}
XNA (Windows) {6D335F3A-9D43-41b4-9D22-F6F17C4BE596}
XNA (XBox) {2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2}
XNA (Zune) {D399B71A-8929-442a-A9AC-8BEC78BB2433}
Silverlight {A1591282-1198-4647-A2B1-27E5FF5F6F3B}
ASP.NET MVC {F85E285D-A4E0-4152-9332-AB1D724D3325}
ASP.NET MVC 4 {E3E379DF-F4C6-4180-9B81-6769533ABE47}
Test {3AC096D0-A1C2-E12C-1390-A8335801FDAB}
Solution Folder {2150E333-8FDC-42A3-9474-1A3956D46DE8}

http://stackoverflow.com/questions/2911565/what-is-the-significance-of-projecttypeguids-tag-in-the-visual-studio-project-fi

From here you reload the project (after you save the project file) and a way you go.

Giving Enum’s descriptions

Enums are very good for giving descriptions to values especially numeric values. By default though a enum value name cannot have spaces. Look at the following which shows an example of no spaces between the first enum value name.

1
2
3
4
enum myEnum
{
   Icannothaveaspace = 1
}

A good way to solve this problem is to use the Description attribute from the System.ComponentModel namespace. We can use this attribute to give each enum value a descriptive meaning.

1
2
3
4
5
6
7
8
9
using System.ComponentModel;

 public enum MyEnum
    {
        [Description("Authorised Contact")]
        AuthorisedContact = 1,
        [Description("Assigned Contact")]
        CoauthorisedContact = 2
    }

From here we can create a extension method that uses reflection to get the description attribute for the selected enum value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static class Extensions
    {
        /// <summary>
        /// Get the description of a enum value using the description attribute
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetEnumDescription(this Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(
                typeof(DescriptionAttribute),
                false);

            if (attributes != null && attributes.Length > 0)
                return attributes[0].Description;
           
            return value.ToString();
        }
    }

To use this, just call GetEnumDescription to get the description of a enum value.

1
2
3
4
5
6
7
8
9
  using Extensions; //Make sure that you use this to get the extension method
 
  public myClass
  {
      public void foo()
      {
          var myDescription = MyEnumValue.GetEnumDescription();
      }
  }

Perform ASP.NET Postback using JQuery

Came across a situation where I needed to disable the ASP.NET button using JQuery when the user clicks on the button, this is so we stop the user from clicking the button twice while the page is doing a postback.

Firstly, add the following JQuery/Javascript code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>

 function autoSubmit()
 {
 <%= ClientScript.GetPostBackEventReference(btnSaveChanges, "") %>;
 }
     $(function () {

           $("#<%= btnSaveChanges.ClientID %>").click(function () {
             $(this).attr("disabled", "true");
             autoSubmit();
           
             return false;
         });
     });
</script>

You can inject the correct generated javascript code from using the function GetPostBackEventReference between the server tags. Bind a click event to the ASP.NET server control which disables the button and call the autosubmit javascript function to perform a post back on the ASP.NET button control.

References

http://msdn.microsoft.com/en-us/library/ms153112.aspx

Elmah and MVC 3 Error Handling

I came into an issue where I have Elmah configured on a ASP.NET MVC 3 website. I notice that the errors were not getting logged or emailed as per my configuration settings in web.config.
I found that MVC3 Error Handling was handling all the errors before Elmah could handle the error and report it correctly.

This link was very helpful:

http://thecodersperspective.posterous.com/how-to-get-elmah-and-mvc3-error-handling-to-p

Also when using Elmah on a production environment, its a good idea to lock down the permissions for the elmah.axd handler so only users in a particular role have access to this file. (Assuming that your website is using forms authentication and role provider)

1
2
3
4
5
6
7
8
 <location path="elmah.axd">
    <system.web>
      <authorization>
        <allow roles="Administrators" />
        <deny users="*" />
      </authorization>
    </system.web>
  </location>

And also make sure that this setting is set to false so its not available to remote users.

1
<security allowRemoteAccess="0" />

Using UserControl.RenderControl()

Perhaps you want to reuse a usercontrol in a email? or you need to generate the html from a usercontrol for a specific purpose?

Firstly, here is the code to render the html from a user control to a string builder object:

1
2
3
4
5
6
7
8
9
   Dim sb As New StringBuilder()
   Using sw As New StringWriter(sb)
      Using htmlTw As New HtmlTextWriter(sw)
          Dim ucUserControl As UCUserControl = LoadControl("myUserControl.ascx")
          ucUserControl .Visible = True
          ucUserControl .Display() 'This is a custom method to do some processing
          ucUserControl .RenderControl(htmlTw)
      End Using
   End Using

Secondly, make sure that you set enableEventValidation=”false” on the page you are generating the html from. You will get an error if this is enabled. You could extend this futher by adding a boolean variable to the page and only disabling Event Validation while rending the control.

Thirdly, override the VerifyRenderingInServerForm method and make it doing “nothing”. If you do not do this, you will get an error saying that you have no form sever tag in your user control.

1
2
3
Public Overrides Sub VerifyRenderingInServerForm(control As System.Web.UI.Control)
'Do nothing
End Sub

Back to Basics – Deleting Cookies using ASP.NET

You cannot instruct ASP.NET to physically remove cookies from the user’s computer so you have to tell the user’s browser to modify the cookie in order to trigger the browser to remove the cookie.

See code example:

1
2
3
4
5
6
7
8
9
HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i {
cookieName = Request.Cookies[i].Name;
aCookie = new HttpCookie(cookieName);
aCookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(aCookie);
}

 

References:

http://msdn.microsoft.com/en-us/library/ms178194.aspx#Y5776

handling multiple submit buttons in one form (asp.net webforms)

I came across an issue where I had the following setup with an asp.net website:

  1. I had a master page with one main form element
  2. I had a submit button in the master page which acts as a search button across the website
  3. I had a login page that also has a submit button

Naturally when the user presses the enter key, it naturally takes the first submit button on the webpage as being the default submit button.   Therefore when a user enters their credentials on the login page, they will always be taken to the search page which is defined by the input button on the master page.

I used jQuery to help solve this issue, you need to do two important things:

1) You need to set a default selector on the input button you want to submit, i.e on the login button I put:

1
 <input class="buttondefault" name="myButton" type="submit" />

 

2) Use the following code (Note this code can easily be refactored to be more efficient):

1
2
3
4
5
6
7
8
9
10
11
12
13
$("form input").keypress(function (e) {
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
//if the search has text then submit that button first
if ($('.buttondefault').length > 0) {
$('.buttondefault').click();
return false;
}
$('#myMasterButtonClass input[type=submit]').click();
return false;
} else {
return true;
}
});

Detect when a session has ended ASP.NET

A problem that I have had in the past is trying to find out when a session has expired within ASP.NET and how to deal with the expired session.   One solution is to add the following code into the Page_Init event.   You can add this event into a class that inherits the Page class so that you can reuse on more than one page or all pages if needed.

1
2
3
4
5
6
7
8
If Not Context.Session Is Nothing Then
If Session.IsNewSession Then
Dim szCookieHeader As String = Request.Headers("Cookie")
If Not String.IsNullOrEmpty(szCookieHeader) And szCookieHeader.IndexOf("ASP.NET_SessionId") &gt;= 0 Then
Response.Redirect("/sessiontimeoutpage.aspx")
End If
End If
End If

This code checks if a new session has been created and if the header has a ASP.NET Session id.   This makes sure that we are checking for a session that has just been created.

Could not load file or assembly ‘NameOfAssemblyGoesHere’

Encounted the following error:

Could not load file or assembly ‘NameOfAssemblyGoesHere’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

The problem happened when deploying a web application to a 64bit server where all the required DLL’s where marked as running only on a 32bit system.   You can change this by setting the target platform to AnyCPU which will solve this issue.   There is another workaround which is setting IIS compatability mode to 32bit, Open a command prompt and navigate to the %systemdrive%\Inetpub\AdminScripts directory.  Type in the command cscript.exe adsutil.vbs set W3SVC/AppPools/Enable32BitAppOnWin64 “true”.