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

No comments:

Post a Comment