Proxy Classes are not created using XmlCommentsImporter

Jun 29, 2009 at 2:16 PM

When I add a Service Reference to my own Service using XmlCommentsImporter, the created Reference.cs is empty. It works on the same client, if I add a Service Reference to WsdlSample.svc. So the problem has something to do with my service. The WSDL contains comments and the only difference to the sample code, that I can detect, is, that I use properties as DataMembers with types, that are themselves DataContracts. Some of my DataContract classes also inhertit from other DataContract classes. In the WsdlSample service there are only string or int properties (except the enumeration).

I also tried to use svcutil with the XmlCommentsImporter, by entering the following in svcutil.exe.config and using the following command line (WCFExtras.dll is in the same directory as svcutil.exe):

  <configSections>
    <section name="xmlComments" type="WCFExtras.Wsdl.Documentation.XmlCommentsConfig, WCFExtras"/>
  </configSections>
  <system.serviceModel>
    <client>
      <metadata>
        <wsdlImporters>
          <extension type="WCFExtras.Wsdl.Documentation.XmlCommentsImporter, WCFExtras, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </wsdlImporters>
      </metadata>
    </client>
  </system.serviceModel>
  <xmlComments format="Default" wrapLongLines="True"/>

svcutil /r:WCFExtras.dll http://localhost/owsmeta/OwsMeta1.svc

I get the following Error:

Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation.  All rights reserved.

Attempting to download metadata from 'http://localhost/owsmeta/OwsMeta1.svc' usi
ng WS-Metadata Exchange or DISCO.
Error: There was an error verifying some XML Schemas generated during export:
Elements with the same name and in the same scope must have the same type.


Error: An error occurred in the tool.

Error: The method or operation is not implemented.

 

Again, if I use http://127.0.0.1/Sample/WsdlSample.svc instead of my service, it works:

Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation.  All rights reserved.

Attempting to download metadata from 'http://127.0.0.1/Sample/WsdlSample.svc' us
ing WS-Metadata Exchange or DISCO.
Generating files...
C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\WsdlSample.cs
C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\output.config

Could it be, that my DataContracts are too complex?

Jul 7, 2009 at 3:40 PM

I digged a bit into this (since I'm having the same problem as you do), and found out that the core problem is with the Utils\WsdlUtil.cs file. The GetUniqueName() method is where the exception gets thrown. The problem is that we are now encountering XmlSchemaComplexContentExtension objects, and those are unhandled by WCFExtras.

What you can do is add a few lines like this:

 

else if (schemaObj is XmlSchemaComplexContentExtension)
{
    string parentName = GetUniqueName(schemaObj.Parent.Parent);
    return parentName + "." + XmlConvert.DecodeName(((XmlSchemaComplexContentExtension) schemaObj).BaseTypeName.Name);
}

 

This will make the service proxy creation work, but not 100% correct: the XML comments for the base class objects and the child class objects will not be produced correctly.

I might look into this in a few weeks when there is less work to do at my workplace, but for now, this will work (at least for me).

Best regards,
Per

Jul 7, 2009 at 8:13 PM

I managed to solve this. This is my diff (in GNU diff, unified format). I'll email it to the author as well and we can hopefully have it fixed by the next release.

You'd use GNU patch to apply this. If you don't know what this program is, it is a very good command-line tool from the UNIX/GNU world. There is a Windows version though, it can be found here: http://gnuwin32.sourceforge.net/packages/patch.htm

Anyway, here is my diff. Cut and paste this to a .diff file. Place it in the folder right above WCFExtras. And run patch -p0 < file.diff (where file.diff is the name of the file you've given it):

Endast i WCFExtras: bin
Endast i WCFExtras: obj
Endast i WCFExtras: README.txt
diff -x '*.csproj' -ur WCFExtras.org/Utils/CodeDomUtils.cs WCFExtras/Utils/CodeDomUtils.cs
--- WCFExtras.org/Utils/CodeDomUtils.cs    2008-12-27 21:03:54.000000000 +0200
+++ WCFExtras/Utils/CodeDomUtils.cs    2009-07-07 21:33:42.290023500 +0300
@@ -37,20 +37,20 @@
             public string Name;
         }
 
-        public static Dictionary<string, CodeTypeMember> EnumerareCodeMembers(CodeCompileUnit unit)
+        public static Dictionary<string, CodeTypeMember> EnumerateCodeMembers(CodeCompileUnit unit)
         {
             Dictionary<string, CodeTypeMember> result = new Dictionary<string, CodeTypeMember>();
             foreach (CodeNamespace ns in unit.Namespaces)
             {
                 foreach (CodeTypeDeclaration decl in ns.Types)
                 {
-                    EnumerareCodeMembers(decl, null, result);
+                    EnumerateCodeMembers(decl, null, result);
                 }
             }
             return result;
         }
 
-        private static void EnumerareCodeMembers(CodeTypeMember member, QualifiedName parentName, Dictionary<string, CodeTypeMember> members)
+        private static void EnumerateCodeMembers(CodeTypeMember member, QualifiedName parentName, Dictionary<string, CodeTypeMember> members)
         {
             QualifiedName memberName = GetUniqueName(member, parentName);
             members[memberName.ToString()] = member;
@@ -59,7 +59,7 @@
             {
                 foreach (CodeTypeMember subMember in decl.Members)
                 {
-                    EnumerareCodeMembers(subMember, memberName, members);
+                    EnumerateCodeMembers(subMember, memberName, members);
                 }
             }
         }
@@ -117,7 +117,8 @@
             return new QualifiedName(ns, name);
         }
 
-        //Extensions
+        #region Extension methods
+
         public static CodeAttributeDeclaration Find(this CodeAttributeDeclarationCollection attributes, params string[] attributeTypes)
         {
             return attributes.Cast<CodeAttributeDeclaration>().FirstOrDefault(attribute => attributeTypes.Contains(attribute.AttributeType.BaseType));
@@ -152,5 +153,7 @@
         {
             return (argument.Value as CodePrimitiveExpression).Value.ToString();
         }
+
+        #endregion
     }
 }
diff -x '*.csproj' -ur WCFExtras.org/Utils/WsdlUtils.cs WCFExtras/Utils/WsdlUtils.cs
--- WCFExtras.org/Utils/WsdlUtils.cs    2008-12-27 21:03:50.000000000 +0200
+++ WCFExtras/Utils/WsdlUtils.cs    2009-07-07 22:08:08.980748800 +0300
@@ -5,6 +5,7 @@
 using System.Web.Services.Description;
 using System.Xml.Schema;
 using System.Xml;
+using System.Diagnostics;
 
 namespace WCFExtras.Utils
 {
@@ -30,7 +31,7 @@
         {
             foreach (XmlSchemaObject schemaObj in schemaItems)
             {
-                string documentation = GetDocumenation(schemaObj);
+                string documentation = GetDocumentation(schemaObj);
                 if (documentation != null)
                 {
                     string uniqueName = GetUniqueName(schemaObj);
@@ -67,22 +68,38 @@
         {
             if (schemaObj is XmlSchemaType)
             {
-                return XmlConvert.DecodeName(((XmlSchemaType)schemaObj).QualifiedName.ToString());
+                return XmlConvert.DecodeName(((XmlSchemaType) schemaObj).QualifiedName.ToString());
             }
             else if (schemaObj is XmlSchemaElement)
             {
-                string parentName = GetUniqueName(schemaObj.Parent.Parent);
-                return parentName + "." + XmlConvert.DecodeName(((XmlSchemaElement)schemaObj).Name);
+                string parentName;
+
+                // For Data Contracts which use inheritance, the XML schemas (XSDs) are a bit more complex.
+                // Instead of the actual "class" node being two level above, it will be four levels above.
+                // We need to handle this here, to properly handle subclasses data contracts.
+                if (schemaObj.Parent.Parent is XmlSchemaComplexContentExtension)
+                {
+                    // Is this nice or what... :-)
+                    parentName = GetUniqueName(schemaObj.Parent.Parent.Parent.Parent);
+                }
+                else
+                {
+                    parentName = GetUniqueName(schemaObj.Parent.Parent);
+                }
+                return parentName + "." + XmlConvert.DecodeName(((XmlSchemaElement) schemaObj).Name);
             }
             else if (schemaObj is XmlSchemaEnumerationFacet)
             {
                 string parentName = GetUniqueName(schemaObj.Parent.Parent);
-                return parentName + "." + XmlConvert.DecodeName(((XmlSchemaEnumerationFacet)schemaObj).Value);
+                return parentName + "." + XmlConvert.DecodeName(((XmlSchemaEnumerationFacet) schemaObj).Value);
             }
-            throw new NotImplementedException();
+
+            throw new NotImplementedException(String.Format(
+                "Unknown schema object detected: {0}, at line number {1}, position {2}",
+                schemaObj.GetType().FullName, schemaObj.LineNumber, schemaObj.LinePosition));
         }
 
-        private static string GetDocumenation(XmlSchemaObject schemaObj)
+        private static string GetDocumentation(XmlSchemaObject schemaObj)
         {
             XmlSchemaAnnotated annotated = schemaObj as XmlSchemaAnnotated;
             if (annotated == null || annotated.Annotation == null)
@@ -93,7 +110,14 @@
                 XmlSchemaDocumentation doc = annotation as XmlSchemaDocumentation;
                 if (doc != null && doc.Markup.Length > 0)
                 {
-                    return doc.Markup[0].Value;
+                    StringBuilder documentation = new StringBuilder();
+
+                    foreach (XmlNode node in doc.Markup)
+                    {
+                        documentation.Append(node.Value);
+                    }
+
+                    return documentation.ToString();
                 }
             }
             return null;
Endast i WCFExtras: WCFExtras.csproj.vspscc
Endast i WCFExtras: WCFExtras.dll
Endast i WCFExtras: WCFExtras.pdb
diff -x '*.csproj' -ur WCFExtras.org/Wsdl/Documentation/XmlCommentsImporter.cs WCFExtras/Wsdl/Documentation/XmlCommentsImporter.cs
--- WCFExtras.org/Wsdl/Documentation/XmlCommentsImporter.cs    2008-12-27 21:03:50.000000000 +0200
+++ WCFExtras/Wsdl/Documentation/XmlCommentsImporter.cs    2009-07-07 22:03:42.558032300 +0300
@@ -103,7 +103,7 @@
 
         private void AddXmlCommentsToDataContracts(ServiceContractGenerationContext context)
         {
-            Dictionary<string, CodeTypeMember> codeMembers = CodeDomUtils.EnumerareCodeMembers(context.ServiceContractGenerator.TargetCompileUnit);
+            Dictionary<string, CodeTypeMember> codeMembers = CodeDomUtils.EnumerateCodeMembers(context.ServiceContractGenerator.TargetCompileUnit);
 
             Dictionary<string, string> documentedItems = new Dictionary<string, string>();
             WsdlUtils.EnumerateDocumentedItems(importer.wsdlDocuments, documentedItems);