Tuesday, May 22, 2012

Re-targeting multiple Asp.Net Web App from 3.5 to 4

I understand it's a bit late to be doing it but better late than never. I got this .Net Framework 3.5 project to convert to .Net Framework 4...

Easiest way I know is: switch a value in a selection box and Visual studio does the changes for you.




However my situation was that I had several configuration files I would have to convert.
I found on msdn, instructions to perform the conversion manually and wrote each step into a quick-and-dirty command line application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;

namespace RetargetFramework4
{
    class Program
    {
        static void Main(string[] args)
        {
            bool deleteWebServerSection = true;

            // justrun param will let if go without interruptions (and WILL execute step 7)
            if (args == null || !args.Any() || args[0] != "justrun")
            {
                // try to make the user give up
                if (!Confirm(@"
This code runs the steps from: http://msdn.microsoft.com/en-us/library/dd483478.aspx on all files ending with .config in the current folder.
Make sure you have them backed up.

Step 1: Make sure that the application currently targets ASP.NET 3.5!

Step 9: If you have customized the Web.config file, and if any customizations refer to custom assemblies or classes, make sure that the assemblies or classes are compatible with the .NET Framework version 4.

Are you sure you want to continue? (y/n)
"))
                    return;

                deleteWebServerSection = ShallPerformStepSeven();
            }

            var configs = Directory.GetFiles(".", "*.config");
            Console.WriteLine("{0}Found {1}", Environment.NewLine, string.Join(", ", configs));

            ProcessConfigs(configs, deleteWebServerSection);

            Console.WriteLine("done!");
        }

        private static bool ShallPerformStepSeven()
        {
            return Confirm(@"
Please note that:
Step 7 is: Delete everything between the system.webserver section start and end tags, but leave the tags themselves.
But in fact retargeting a Web project from the project property tab with Visual Studio 2010 does NOT remove all child elements!

Plus, be aware of these:
http://msdn.microsoft.com/en-us/library/46c5ddfy.aspx
http://weblogs.asp.net/mschwarz/archive/2008/11/07/appcmd-migrate-and-http-error-500-22-things-you-can-try.aspx
http://stackoverflow.com/questions/3400116/deploying-asp-net-mvc-app-to-iis7-net-4-0-environment-images-and-css-dont-loa

Do you want to execute this step? (y/n)
");
        }

        private static void ProcessConfigs(IEnumerable<string> configs, bool deleteWebServerSection)
        {
            foreach (var config in configs)
            {
                // 2 - Open the Web.config file in the application root.
                var xConfig = XDocument.Load(config);

                // 3 - In the configSections section, remove the sectionGroup element that is named "system.web.extensions".
                var webExtensions = xConfig
                    .XPathSelectElements("configuration/configSections/sectionGroup[@name='system.web.extensions']")
                    .FirstOrDefault();
                if (webExtensions != null)
                    webExtensions.Remove();

                // 4 - In the system.web section, in the compilation collection, remove every add element that refers to an assembly of the .NET Framework.
                var assembliesAdd = from a in xConfig
                                .XPathSelectElements("configuration/system.web/compilation/assemblies/add")
                                    let assembly = (string)a.Attribute("assembly")
                                    where assembly != null && assembly.StartsWith("System.")
                                    select a;
                if (assembliesAdd.Any())
                    assembliesAdd.Remove();

                // 5 - Add a targetFramework attribute to the compilation element in the system.web section, as shown in the following example:
                var compilation = xConfig.XPathSelectElements("configuration/system.web/compilation").FirstOrDefault();
                if (compilation != null)
                    compilation.SetAttributeValue("targetFramework", "4.0");

                // 6 A - In the opening tag for the pages section, add a controlRenderingCompatibility attribute, as shown in the following example:
                var pages = xConfig.XPathSelectElements("configuration/system.web/pages").FirstOrDefault();
                if (pages != null)
                    pages.SetAttributeValue("controlRenderingCompatibilityVersion", "3.5");

                // 6 B - In the system.codedom section, in the compilers collection, remove the compiler elements for c# and vb.
                var codedom = from a in xConfig
                                .XPathSelectElements("//system.codedom/compilers/compiler")
                              let language = (string)a.Attribute("language")
                              where language != null
                              && (language.StartsWith("c#") || language.StartsWith("vb"))
                              select a;
                if (codedom.Any())
                    codedom.Remove();

                // 7 - Delete everything between the system.webserver section start and end tags, but leave the tags themselves.
                if (deleteWebServerSection)
                {
                    xConfig.XPathSelectElements("configuration/system.webServer/*").Remove();
                }

                // 8 - Delete everything between the runtime section start and end tags, but leave the tags themselves.
                xConfig.XPathSelectElements("configuration/runtime/*").Remove();

                xConfig.Save(config, SaveOptions.None);
                Console.WriteLine("Finished with {0}.", config);
            }
        }

        private static bool Confirm(string message)
        {
            Console.WriteLine(message);
            return Console.ReadKey().KeyChar == 'y';
        }
    }
}

Use it in a script if you'd like, to be able to re-target an ASP.Net Web Application from version 3.5 to 4.0.
It could be useful to re-target many applications, with multiple web.configs, from the command prompt: 

for /f %a in ('dir /s /b web.config') do cd %a\.. & RetargetFramework4.exe justrun 
Note that if you are calling it from within a batch (.bat or .cmd) script file, % becomes %% 

Make sure your config files are not marked as readonly (they probably will if you are using Source control and have not them checked out). So check them out or remove the readonly attribute: 

attrib -r web.config

Sunday, May 20, 2012

Google Maps vs Bing Maps


After planning trips for about 30 countries, road trips or not, my browser learned that when I press m, I'm looking for Google Maps:




This time, my trip will go through Bosnia and Herzegovina, and there was a problem though. Google Maps won't find routes through it.


We could not calculate directions between Belgrade, Serbia and Sarajevo, Bosnia and Herzegovina.

Another test showed that not only from outside Bosnia but also within the country:

We could not calculate directions between Banja Luka, Bosnia and Herzegovina and Sarajevo, Bosnia and Herzegovina.

That's when I gave a try to Bing Maps. It was a moment of surprise to see the route calculated until I noticed it looked quite weird:

Belgrade - Sarajevo with Bing Maps (http://binged.it/JI0BkK)
The route created clearly was too long, having 394 kilometers (244 miles).

After a quick research I found on Sarajevo wikitravel:

From Belgrade (Serbia) - taking direction to Sabac - Zvornik - Vlasenica - Sokolac - Sarajevo.

Using this tip, I decided to "help" Bing Maps by adding these locations on the way. The result, the least I can say, was funny! :)


Belgrade - Sarajevo and more with Bing Maps (http://binged.it/JHYXQ5)


A way of 632 kilometers (392 miles), completely insane!

The situation is that Google Maps won't give directions with a location inside Bosnia and Bing Maps seems to make fun of me. Finally, I got it surprisingly with Michelin.


]
Belgrade - Sarajevo with Via Michelin (http://tinyurl.com/7qt94ya)

A route with 305 kilometers (189 miles)

Today's battle Microsoft vs Google, the winner was Michelin!