From 1af3eab71a8458838882b1b257cc9dd058500edd Mon Sep 17 00:00:00 2001 From: Dominik Baran Date: Fri, 29 Mar 2024 13:35:28 +0100 Subject: [PATCH 1/2] feat(wsdl-asmx): add support to WSDL for base types --- .../Wsdl/Services/IComplexBaseTypeService.cs | 28 ++++++++++++++++ src/SoapCore.Tests/Wsdl/WsdlTests.cs | 18 +++++++++++ src/SoapCore/Meta/MetaBodyWriter.cs | 32 ++++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs diff --git a/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs b/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs new file mode 100644 index 00000000..1681e82e --- /dev/null +++ b/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs @@ -0,0 +1,28 @@ +using System.ServiceModel; + +namespace SoapCore.Tests.Wsdl.Services; + +[ServiceContract] +public interface IComplexBaseTypeService +{ + [OperationContract] + DerivedType Method(DerivedType argument); +} + +public class ComplexBaseTypeService : IComplexBaseTypeService +{ + public DerivedType Method(DerivedType argument) + { + return new DerivedType(); + } +} + +public class BaseType +{ + public string BaseName { get; set; } +} + +public class DerivedType : BaseType +{ + public string DerivedName { get; set; } +} diff --git a/src/SoapCore.Tests/Wsdl/WsdlTests.cs b/src/SoapCore.Tests/Wsdl/WsdlTests.cs index 0edf9a9f..2c908de2 100644 --- a/src/SoapCore.Tests/Wsdl/WsdlTests.cs +++ b/src/SoapCore.Tests/Wsdl/WsdlTests.cs @@ -1135,6 +1135,24 @@ public async Task CheckDataContractKnownTypeAttributeServiceWsdl() Assert.IsNotNull(schemaElement.XPathSelectElement("//xsd:element[@name='ComplexInheritanceModelInputB' and @type='tns:ComplexInheritanceModelInputB']", nm)); } + [TestMethod] + public void CheckComplexBaseTypeServiceWsdl() + { + StartService(typeof(ComplexBaseTypeService)); + var wsdl = GetWsdlFromAsmx(); + StopServer(); + Assert.IsNotNull(wsdl); + + var root = XElement.Parse(wsdl); + var nm = Namespaces.CreateDefaultXmlNamespaceManager(false); + + var derivedTypeContent = root.XPathSelectElement("//xsd:complexType[@name='DerivedType']/xsd:complexContent[@mixed='false']/xsd:extension[@base='tns:BaseType']/xsd:sequence/xsd:element[@name='DerivedName' and @type='xsd:string' and not(@nillable)]", nm); + Assert.IsNotNull(derivedTypeContent); + + var baseTypeContent = root.XPathSelectElement("//xsd:complexType[@name='BaseType']/xsd:sequence/xsd:element[@name='BaseName' and @type='xsd:string' and not(@nillable)]", nm); + Assert.IsNotNull(baseTypeContent); + } + [TestCleanup] public void StopServer() { diff --git a/src/SoapCore/Meta/MetaBodyWriter.cs b/src/SoapCore/Meta/MetaBodyWriter.cs index eed0faff..62dcf226 100644 --- a/src/SoapCore/Meta/MetaBodyWriter.cs +++ b/src/SoapCore/Meta/MetaBodyWriter.cs @@ -744,6 +744,15 @@ private void AddService(XmlDictionaryWriter writer) } } + private bool HasBaseType(Type type) + { + var isArrayType = type.IsArray || typeof(IEnumerable).IsAssignableFrom(type); + + var baseType = type.GetTypeInfo().BaseType; + + return !isArrayType && !type.IsEnum && !type.IsPrimitive && !type.IsValueType && baseType != null && !baseType.Name.Equals("Object"); + } + private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuild) { var toBuildType = toBuild.Type; @@ -760,6 +769,21 @@ private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuil writer.WriteAttributeString("name", toBuildName); } + var hasBaseType = HasBaseType(toBuildType); + if (hasBaseType) + { + writer.WriteStartElement("complexContent", Namespaces.XMLNS_XSD); + + writer.WriteAttributeString("mixed", "false"); + + writer.WriteStartElement("extension", Namespaces.XMLNS_XSD); + + var typeName = toBuildType.BaseType.GetSerializedTypeName(); + writer.WriteAttributeString("base", $"tns:{typeName}"); + + _complexTypeToBuild.Enqueue(new TypeToBuild(toBuildType.BaseType)); + } + if (toBuildType.IsArray) { writer.WriteStartElement("sequence", Namespaces.XMLNS_XSD); @@ -777,7 +801,7 @@ private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuil if (!isWrappedBodyType) { var propertyOrFieldMembers = toBuildBodyType.GetPropertyOrFieldMembers() - .Where(mi => !mi.IsIgnored()).ToList(); + .Where(mi => !mi.IsIgnored() && mi.DeclaringType == toBuildType).ToList(); var elements = propertyOrFieldMembers.Where(t => !t.IsAttribute()).ToList(); if (elements.Any()) @@ -836,6 +860,12 @@ private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuil } } + if (hasBaseType) + { + writer.WriteEndElement(); // xs:extension + writer.WriteEndElement(); // xs:complexContent + } + writer.WriteEndElement(); // complexType if (isWrappedBodyType) From 553e2fbcec399427e10b34417fc5e0ff91073239 Mon Sep 17 00:00:00 2001 From: Dominik Baran Date: Fri, 29 Mar 2024 14:22:43 +0100 Subject: [PATCH 2/2] fix: generic base types --- .../Wsdl/Services/IComplexBaseTypeService.cs | 11 ++++++++--- src/SoapCore.Tests/Wsdl/WsdlTests.cs | 6 ++++++ src/SoapCore/Meta/BodyWriterExtensions.cs | 11 +++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs b/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs index 1681e82e..e29f0ff4 100644 --- a/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs +++ b/src/SoapCore.Tests/Wsdl/Services/IComplexBaseTypeService.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.ServiceModel; namespace SoapCore.Tests.Wsdl.Services; @@ -6,14 +7,14 @@ namespace SoapCore.Tests.Wsdl.Services; public interface IComplexBaseTypeService { [OperationContract] - DerivedType Method(DerivedType argument); + DerivedTypeList Method(DerivedTypeList argument); } public class ComplexBaseTypeService : IComplexBaseTypeService { - public DerivedType Method(DerivedType argument) + public DerivedTypeList Method(DerivedTypeList argument) { - return new DerivedType(); + return new DerivedTypeList(); } } @@ -26,3 +27,7 @@ public class DerivedType : BaseType { public string DerivedName { get; set; } } + +public class DerivedTypeList : List +{ +} diff --git a/src/SoapCore.Tests/Wsdl/WsdlTests.cs b/src/SoapCore.Tests/Wsdl/WsdlTests.cs index 2c908de2..09ed16e7 100644 --- a/src/SoapCore.Tests/Wsdl/WsdlTests.cs +++ b/src/SoapCore.Tests/Wsdl/WsdlTests.cs @@ -1151,6 +1151,12 @@ public void CheckComplexBaseTypeServiceWsdl() var baseTypeContent = root.XPathSelectElement("//xsd:complexType[@name='BaseType']/xsd:sequence/xsd:element[@name='BaseName' and @type='xsd:string' and not(@nillable)]", nm); Assert.IsNotNull(baseTypeContent); + + var listDerivedTypeMethodResponse = root.XPathSelectElement("//xsd:element[@name='MethodResponse']/xsd:complexType/xsd:sequence/xsd:element[@name='MethodResult' and @type='tns:ArrayOfDerivedType' and @nillable='true']", nm); + Assert.IsNotNull(listDerivedTypeMethodResponse); + + var listDerivedType = root.XPathSelectElement("//xsd:complexType[@name='ArrayOfDerivedType']/xsd:sequence/xsd:element[@name='DerivedType' and @type='tns:DerivedType' and @nillable='true' and @minOccurs='0' and @maxOccurs='unbounded']", nm); + Assert.IsNotNull(listDerivedType); } [TestCleanup] diff --git a/src/SoapCore/Meta/BodyWriterExtensions.cs b/src/SoapCore/Meta/BodyWriterExtensions.cs index b937a340..19f5f017 100644 --- a/src/SoapCore/Meta/BodyWriterExtensions.cs +++ b/src/SoapCore/Meta/BodyWriterExtensions.cs @@ -197,6 +197,9 @@ public static string GetSerializedTypeName(this Type type) { var namedType = type; bool isNullableArray = false; + var isGenericType = type.IsGenericType; + var isBaseTypeGeneric = type.BaseType is { IsGenericType: true }; + if (type.IsArray) { namedType = type.GetElementType(); @@ -207,9 +210,9 @@ public static string GetSerializedTypeName(this Type type) isNullableArray = true; } } - else if (typeof(IEnumerable).IsAssignableFrom(type) && type.IsGenericType) + else if (typeof(IEnumerable).IsAssignableFrom(type) && (isGenericType || isBaseTypeGeneric)) { - namedType = GetGenericType(type); + namedType = isGenericType ? GetGenericType(type) : GetGenericType(type.BaseType); var underlyingType = Nullable.GetUnderlyingType(namedType); if (underlyingType != null) { @@ -225,9 +228,9 @@ public static string GetSerializedTypeName(this Type type) typeName = xmlTypeAttribute.TypeName; } - if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) && type.IsGenericType)) + if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) && (isGenericType || isBaseTypeGeneric))) { - if (namedType.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) && type.IsGenericType)) + if (namedType.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) && (isGenericType || isBaseTypeGeneric))) { typeName = GetSerializedTypeName(namedType); }