.NET Web Services, Objects, WSDL.exe, and Reference.cs

October 15, 2009 § Leave a comment

This problem is annoying.

If you make a .NET web service and you add the web reference to another project, Visual Studio runs WSDL.exe in the background to create a Reference.cs file – which is awesome (sort of). The Reference.cs file gives you the ability to call your web service just as if it were any other .NET class you have a reference to, which again, is awesome (sort of).

The problem arises when you have a class defined that is accepted as a parameter to, or returned from, a method in your web service. WSDL.exe generates a class definition for this class in the Reference.cs file, which is great if you don’t want to reference the actual class.

The standard solution to this problem is much as Bruce Johnson describes, which is to hand-edit the Reference.cs file to remove the generated class, and add a “using” statement or fully qualify the class name to point to the correct (the original) namespace.

Unfortunately, this means that every time you need to update the web reference, and the Reference.cs file is regenerated, you need to hand edit the Reference.cs file, which is a huge waste of time and kind of a pain in the butt.

There is apparently a way to use SchemaImporterExtensions to affect the way that WSDL.exe generates the Reference.cs file, but to be honest, it looked really confusing and also difficult to maintain. I didn’t really attempt to do it, though, so maybe it’s easier than it looks.

So, after having to deal with this on a project I’m working with currently, I decided that it would make sense to automate this annoying problem. I built an executable that accepts, on the command line, the path to a dll, and the path to a Reference.cs file. The executable looks for any public classes or enums that are defined in both the dll and the Reference.cs file, removes the class declaration from the Reference.cs file, and fully qualifies the class references.

prebuildI then changed the project properties (in the project that contains the web references) to add several pre-build commands. The commands call the executable with whatever combination of dll’s and Reference.cs files. This way, you can update the web reference as often as necessary, and before the project builds, the executable will run and fix all the issues in the Reference.cs file.

This code is kind of hacked together, but I really had a hard time justifying spending much time on polishing it, so I’m just going to post it as is. It’ll only work on C#, and as far as I know, there aren’t any bugs, although you might have some. If you use it and find anything wrong with it, leave a comment, and I’ll update the code.

Anyway, here’s the code – I’m kind of surprised I couldn’t find something to do this already, so hopefully it’ll help you.

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

using System.IO;

using System.Text.RegularExpressions;

 

namespace WebServiceReferenceResolver

{

    class Program

    {

        /// <summary>

        /// Mains the specified args.

        /// </summary>

        /// <param name=”args”>The args.</param>

        static void Main(string[] args)

        {

            if (args.Length != 2)

            {

                Console.WriteLine(“Correct Usage: WebServiceReferenceResolver [ASSEMBLYNAME] [REFERENCE.CS]”);

                return;

            }

 

            try

            {

                string assemblyName = new FileInfo(args[0]).FullName;

                string referenceCsName = new FileInfo(args[1]).FullName;

 

                List<Type> classList = GetPublicClasses(assemblyName);

 

                StreamReader sr = new StreamReader(referenceCsName);

                string referenceCsSource = sr.ReadToEnd();

                sr.Close();

 

                // remove class definitions

                for (int i = classList.Count – 1; i >= 0; i–)

                {

                    bool typeFound = false;

                    referenceCsSource = RemoveClassDeclaration(referenceCsSource, classList[i], ref typeFound);

 

                    if (!typeFound)

                    {

                        classList.RemoveAt(i);

                    }

                }

 

                // properly resolve class references

                foreach (Type t in classList)

                {

                    referenceCsSource = ResolveClassReference(referenceCsSource, t);

                }

 

                StreamWriter sw = new StreamWriter(referenceCsName, false);

                sw.Write(referenceCsSource);

                sw.Close();

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.ToString());

            }

        }

 

        /// <summary>

        /// Gets the public classes.

        /// </summary>

        /// <param name=”assemblyName”>Name of the assembly.</param>

        /// <returns></returns>

        private static List<Type> GetPublicClasses(string assemblyName)

        {

            Assembly assembly = Assembly.LoadFile(assemblyName);

 

            Type[] types = new Type[0];

            try

            {

                types = assembly.GetTypes();

            }

            catch (ReflectionTypeLoadException rtle)

            {

                types = rtle.Types;

            }

 

            List<Type> returnList= new List<Type>();

            foreach(Type t in types)

            {

                if (t != null && t.IsPublic)

                {

                    returnList.Add(t);

                }

            }

 

            return returnList;

        }

 

        /// <summary>

        /// Removes the class declaration.

        /// </summary>

        /// <param name=”referenceCsSource”>The reference cs source.</param>

        /// <param name=”t”>The t.</param>

        /// <param name=”typeFound”>if set to <c>true</c> [type found].</param>

        /// <returns></returns>

        private static string RemoveClassDeclaration(string referenceCsSource, Type t, ref bool typeFound)

        {

            int startIndex = -1;

            typeFound = false;

 

            Regex r = new Regex(“(class|enum) “ + t.Name + ” ({|:)”);

            Match m = r.Match(referenceCsSource);

            if (m != null && m.Index != 0)

            {

                startIndex = m.Index;

            }

 

            if (startIndex != -1)

            {

                int firstBracketIndex = referenceCsSource.IndexOf(“{“, startIndex);

 

                int lastBracketIndex = referenceCsSource.LastIndexOf(“}”, startIndex) + 1;

                int lastSemicolonIndex = referenceCsSource.LastIndexOf(“;”, startIndex) + 1;

 

                if (lastBracketIndex > lastSemicolonIndex)

                {

                    startIndex = lastBracketIndex;

                }

                else

                {

                    startIndex = lastSemicolonIndex;

                }

 

                int characterIndex = firstBracketIndex + 1;

                int bracketIndexCount = 1;

                while (characterIndex < referenceCsSource.Length && bracketIndexCount > 0)

                {

                    if (referenceCsSource[characterIndex] == ‘{‘)

                    {

                        bracketIndexCount++;

                    }

                    else if (referenceCsSource[characterIndex] == ‘}’)

                    {

                        bracketIndexCount–;

                    }

 

                    characterIndex++;

                }

 

                if (characterIndex < referenceCsSource.Length)

                {

                    // remove it

                    typeFound = true;

                    referenceCsSource = referenceCsSource.Remove(startIndex, characterIndex – startIndex);

                }

            }

 

            return referenceCsSource;

        }

 

        /// <summary>

        /// Resolves the class reference.

        /// </summary>

        /// <param name=”referenceCsSource”>The reference cs source.</param>

        /// <param name=”t”>The t.</param>

        /// <returns></returns>

        private static string ResolveClassReference(string referenceCsSource, Type t)

        {

            // yeah, this is tacky, but whatever

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(” {0} “, t.Name));

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(” {0}[“, t.Name));

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(” {0}(“, t.Name));

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(“({0} “, t.Name));

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(“({0}[“, t.Name));

            referenceCsSource = ResolveClassReference(referenceCsSource, t, string.Format(“({0})”, t.Name));

 

            return referenceCsSource;

        }

 

        /// <summary>

        /// Resolves the class reference.

        /// </summary>

        /// <param name=”referenceCsSource”>The reference cs source.</param>

        /// <param name=”t”>The t.</param>

        /// <param name=”matchString”>The match string.</param>

        /// <returns></returns>

        private static string ResolveClassReference(string referenceCsSource, Type t, string matchString)

        {

            int index = 0;

            while (index != -1)

            {

                index = referenceCsSource.IndexOf(matchString, index);

                if (index != -1)

                {

                    referenceCsSource = referenceCsSource.Insert(index + 1, string.Format(“{0}.”, t.Namespace));

                    index += t.Namespace.Length + matchString.Length;

                }

            }

 

            return referenceCsSource;

        }

    }

}

Advertisements

Tagged: , , , , , , , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

What’s this?

You are currently reading .NET Web Services, Objects, WSDL.exe, and Reference.cs at Mike Vallotton's Blog.

meta

%d bloggers like this: