Thursday, December 30, 2010

Upgrade from Asp.Net MVC 1 to MVC 2 - how to and issues with JsonRequestBehavior

Goal

Upgrade your MVC 1 app to MVC 2

Issues

You may get errors about your Json data being returned via a GET request violating security principles - we also address this here. This post is not intended to delve into why the Json GET request is or may be an issue, just how to resolve it as part of upgrading from MVC1 to 2.

Solution

First remove all references from your projects to the MVC 1 dll and replace it with the MVC 2 dll. Now update your web.config file in your web app root folder by simply changing references to assembly="System.Web.Mvc, Version 1.0.0.0 to Version 2.0.0.0, there are a couple of references in your config file, here are probably most of them you may have:
        <compilation debug="true" defaultLanguage="c#">
           
        masterPageFile="~/Views/Masters/CRMTemplate.master" pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
            <controls>
                System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validateRequest="False">

Secondly, if you return Json objects from an ajax call via the GET method you ahve several options to fix this depending on your situation:
1. The simplest, as in my case I did this for an internal web app, you may simply do:
            return Json(myObject, JsonRequestBehavior.AllowGet);

2. In Mvc if you have a controller base you could wrap the Json method with:
        public new JsonResult Json(object data)
        {
            return Json(data, "application/json", JsonRequestBehavior.AllowGet);           
        }

3. The most work would be to decorate your Actions with:
        [AcceptVerbs(HttpVerbs.Get)]

4. Another tnat is also a lot of work that needs to be done to every ajax call returning Json is:
                            msg = $.ajax({ url: $('#ajaxGetSampleUrl').val(), dataType: 'json', type: 'POST', async: false, data: { name: theClass }, success: function(data, result) { if (!result) alert('Failure to retrieve the Sample Data.'); } }).responseText;

This should cover all the issues you may run into when upgrading. Let me kow if you run into any other ones.

jQuery 1.4.4 - issue with attr('selected', null)

Issue:
The code below worked before under version jQuery 1.4.2 but when I upgraded to version 1.4.4 it no longer worked as expected - it did not unselect the list box item, only setting "selectd" worked:
        _handleClick: function(elem) {
            var self = this; var initElem = this.element;
            var checked = $(elem).attr('checked');
            var myId = elem.attr('id').replace(initElem.attr('id') + '_chk_', '');
            initElem.children('option[value=' + myId + ']').attr('selected', function() {
                if (checked) {
                    return 'selected';
                } else { return null; }
            });

            if ($.isFunction(self.options.onItemSelected)) {
                try {
                    self.options.onItemSelected(elem, initElem.children('option').get());
                } catch (ex) {
                    if (self.options.allowDebug)
                        alert('select function failed: ' + ex.Description);
                }
            }
        },
Solution:
Under jQuery 1.4.4 you need to explicitly remove the attribute as in "removeAttr('selected'):
        _handleClick: function(elem) {
            var self = this; var initElem = this.element;
            var checked = $(elem).is(':checked');
            var myId = elem.attr('id').replace(initElem.attr('id') + '_chk_', '');
            if (checked) {
                initElem.children('option[value=' + myId + ']').attr('selected', 'selected');
            } else {
                initElem.children('option[value=' + myId + ']').removeAttr('selected');
            }
            if ($.isFunction(self.options.onItemSelected)) {
                try {
                    self.options.onItemSelected(elem, initElem.children('option').get());
                } catch (ex) {
                    if (self.options.allowDebug)
                        alert('select function failed: ' + ex.Description);
                }
            }
        },

Tuesday, December 14, 2010

After restoring a SQL Server database from another server - get login fails

Issue:

After you have restored a sql server database from another server, lets say from production to a Q/A environment, you get the "Login Fails" message for your service account.

Reason:

User logon information is stored in the syslogins table in the master database. By changing servers, or by altering this information by rebuilding or restoring an old version of the master database, the information may be different from when the user database dump was created. If logons do not exist for the users, they will receive an error indicating "Login failed" while attempting to log on to the server. If the user logons do exist, but the SUID values (for 6.x) or SID values (for 7.0) in master..syslogins and the sysusers table in the user database differ, the users may have different permissions than expected in the user database.

Solution:

Links a user entry in the sys.database_principals system catalog view in the current database to a SQL Server login of the same name. If a login with the same name does not exist, one will be created. Examine the result from the Auto_Fix statement to confirm that the correct link is in fact made. Avoid using Auto_Fix in security-sensitive situations.

When you use Auto_Fix, you must specify user and password if the login does not already exist, otherwise you must specify user but password will be ignored. login must be NULL. user must be a valid user in the current database. The login cannot have another user mapped to it.
execute the following stored procedure, in this example the login user name is "MyUser"
exec sp_change_users_login 'Auto_Fix', 'MyUser'

NOTE:
sp_change_users_login cannot be used with a SQL Server login created from a Windows principal or with a user created by using CREATE USER WITHOUT LOGIN.After restoring a SQL Server database from another server - get login fails

Thursday, December 2, 2010

Maximum request length exceeded.

Issue:
The file upload size on your machine or web app is too small. The default file upload size for IIS is 4096K. The file size the user is trying to add can be retrieved (using the ELMAH log here) by looking at the Content-Length header property:

HTTP_CONNECTION:keep-alive HTTP_KEEP_ALIVE:115 HTTP_CONTENT_LENGTH:6414851

In this example it is just over 6Mb

Solution:
You can add/update the HttpRuntime property in the machine or web config files, depending on at what level you want to set the size. For web-server level, impacting all web-apps running on that machine specify the following in the node of the machine.config file to allow less than 1Mb:

      <system.web>
<httpRuntime maxRequestLength="1000" />

If you want to set it, bigger, smaller or block at the web-app level you can specify it by adding the property above to the web.config file of that web app and it will only affect that web app.

For example I would like to maintain the default 4096K size limitation on my server except for the accounting web app and therefore set the accounting web-sites virtual directory web-config file to handle 8Mb file uploads:

      <system.web>
<httpRuntime maxRequestLength="8192" />

Resetting/Restoring the Windows PATH

Problem:

For some reason you lost your Windows PATH settings and want to restore the previous version:

Solution:

First of you may reset it to the original setting to:

Windows XP:     C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;{plus program paths}
Windows 7:     C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;{plus program paths}

Alternatively you could look for some previous version of the path in the registry. In Windows 7 you would look for "Path" name under Computer\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment. If you do not see the previous version of what you would expect, continue down the list "ControlSet002", "..03", etc. The current version will be under Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\....