diff --git a/src/System.Private.ServiceModel/src/Resources/Strings.resx b/src/System.Private.ServiceModel/src/Resources/Strings.resx index f1048f531ac..fe2720796b0 100644 --- a/src/System.Private.ServiceModel/src/Resources/Strings.resx +++ b/src/System.Private.ServiceModel/src/Resources/Strings.resx @@ -1932,4 +1932,22 @@ '{0}' is an invalid XmlNodeType. - \ No newline at end of file + + RPC Message {1} in operation {0} must have a single MessageBodyMember. + + + Type {0} cannot inherit from any class other than object to be used as body object in RPC style. + + + Type {0} implements interface {1} which is not supported for body object in RPC style. + + + Message {0} must not have headers to be used in RPC encoded style. + + + Part {1}:{0} is repeating and is not supported in Soap Encoding. + + + XmlSerializer attribute {0} is not valid in {1}. Only SoapElement attribute is supported. + + diff --git a/src/System.Private.ServiceModel/src/System/ServiceModel/Description/XmlSerializerOperationBehavior.cs b/src/System.Private.ServiceModel/src/System/ServiceModel/Description/XmlSerializerOperationBehavior.cs index 24963c64840..4c958796f2f 100644 --- a/src/System.Private.ServiceModel/src/System/ServiceModel/Description/XmlSerializerOperationBehavior.cs +++ b/src/System.Private.ServiceModel/src/System/ServiceModel/Description/XmlSerializerOperationBehavior.cs @@ -166,6 +166,20 @@ void IOperationBehavior.ApplyClientBehavior(OperationDescription description, Cl proxy.FaultFormatter = (IClientFaultFormatter)CreateFaultFormatter(proxy.FaultContractInfos); } + public Collection GetXmlMappings() + { + var mappings = new Collection(); + if (OperationReflector.Request != null && OperationReflector.Request.HeadersMapping != null) + mappings.Add(OperationReflector.Request.HeadersMapping); + if (OperationReflector.Request != null && OperationReflector.Request.BodyMapping != null) + mappings.Add(OperationReflector.Request.BodyMapping); + if (OperationReflector.Reply != null && OperationReflector.Reply.HeadersMapping != null) + mappings.Add(OperationReflector.Reply.HeadersMapping); + if (OperationReflector.Reply != null && OperationReflector.Reply.BodyMapping != null) + mappings.Add(OperationReflector.Reply.BodyMapping); + + return mappings; + } // helper for reflecting operations internal class Reflector @@ -226,6 +240,7 @@ internal class OperationReflector internal readonly OperationDescription Operation; internal readonly XmlSerializerFormatAttribute Attribute; + internal readonly bool IsEncoded; internal readonly bool IsRpc; internal readonly bool IsOneWay; internal readonly bool RequestRequiresSerialization; @@ -243,13 +258,14 @@ internal OperationReflector(Reflector parent, OperationDescription operation, Xm Fx.Assert(operation != null, ""); Fx.Assert(attr != null, ""); - OperationFormatter.Validate(operation, attr.Style == OperationFormatStyle.Rpc, false/*IsEncoded*/); + OperationFormatter.Validate(operation, attr.Style == OperationFormatStyle.Rpc, attr.IsEncoded); _parent = parent; this.Operation = operation; this.Attribute = attr; + IsEncoded = attr.IsEncoded; this.IsRpc = (attr.Style == OperationFormatStyle.Rpc); this.IsOneWay = operation.Messages.Count == 1; @@ -342,7 +358,7 @@ internal void EnsureMessageInfos() { if (knownType == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxKnownTypeNull, Operation.Name))); - _parent._importer.IncludeType(knownType); + _parent._importer.IncludeType(knownType, IsEncoded); } _request = CreateMessageInfo(this.Operation.Messages[0], ":Request"); // We don't do the following check at Net Native runtime because XmlMapping.XsdElementName @@ -389,9 +405,8 @@ private MessageInfo CreateMessageInfo(MessageDescription message, string key) if (message.IsUntypedMessage) return null; MessageInfo info = new MessageInfo(); - bool isEncoded = false; if (message.IsTypedMessage) - key = message.MessageType.FullName + ":" + isEncoded + ":" + IsRpc; + key = message.MessageType.FullName + ":" + IsEncoded + ":" + IsRpc; XmlMembersMapping headersMapping = LoadHeadersMapping(message, key + ":Headers"); info.SetHeaders(_parent._generation.AddSerializer(headersMapping)); MessagePartDescriptionCollection rpcEncodedTypedMessgeBodyParts; @@ -426,8 +441,17 @@ private void CreateHeaderDescriptionTable(MessageDescription message, MessageInf else { string headerName, headerNs; - headerName = memberMapping.XsdElementName; - headerNs = memberMapping.Namespace; + if (IsEncoded) + { + headerName = memberMapping.TypeName; + headerNs = memberMapping.TypeNamespace; + } + else + { + headerName = memberMapping.XsdElementName; + headerNs = memberMapping.Namespace; + } + if (headerName != header.Name) { if (message.MessageType != null) @@ -454,11 +478,23 @@ private XmlMembersMapping LoadBodyMapping(MessageDescription message, string map MessagePartDescription returnPart; string wrapperName, wrapperNs; MessagePartDescriptionCollection bodyParts; - rpcEncodedTypedMessageBodyParts = null; - returnPart = OperationFormatter.IsValidReturnValue(message.Body.ReturnValue) ? message.Body.ReturnValue : null; - bodyParts = message.Body.Parts; - wrapperName = message.Body.WrapperName; - wrapperNs = message.Body.WrapperNamespace; + if (IsEncoded && message.IsTypedMessage && message.Body.WrapperName == null) + { + MessagePartDescription wrapperPart = GetWrapperPart(message); + returnPart = null; + rpcEncodedTypedMessageBodyParts = bodyParts = GetWrappedParts(wrapperPart); + wrapperName = wrapperPart.Name; + wrapperNs = wrapperPart.Namespace; + } + else + { + rpcEncodedTypedMessageBodyParts = null; + returnPart = OperationFormatter.IsValidReturnValue(message.Body.ReturnValue) ? message.Body.ReturnValue : null; + bodyParts = message.Body.Parts; + wrapperName = message.Body.WrapperName; + wrapperNs = message.Body.WrapperNamespace; + } + bool isWrapped = (wrapperName != null); bool hasReturnValue = returnPart != null; int paramCount = bodyParts.Count + (hasReturnValue ? 1 : 0); @@ -470,22 +506,65 @@ private XmlMembersMapping LoadBodyMapping(MessageDescription message, string map XmlReflectionMember[] members = new XmlReflectionMember[paramCount]; int paramIndex = 0; if (hasReturnValue) - members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(returnPart, IsRpc, isWrapped); + members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(returnPart, IsRpc, IsEncoded, isWrapped); for (int i = 0; i < bodyParts.Count; i++) - members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(bodyParts[i], IsRpc, isWrapped); + members[paramIndex++] = XmlSerializerHelper.GetXmlReflectionMember(bodyParts[i], IsRpc, IsEncoded, isWrapped); if (!isWrapped) wrapperNs = ContractNamespace; return ImportMembersMapping(wrapperName, wrapperNs, members, isWrapped, IsRpc, mappingKey); } + private MessagePartDescription GetWrapperPart(MessageDescription message) + { + if (message.Body.Parts.Count != 1) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxRpcMessageMustHaveASingleBody, Operation.Name, message.MessageName))); + + MessagePartDescription bodyPart = message.Body.Parts[0]; + Type bodyObjectType = bodyPart.Type; + if (bodyObjectType.BaseType != null && bodyObjectType.BaseType != typeof(object)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInherited, bodyObjectType.FullName))); + if (typeof(IEnumerable).IsAssignableFrom(bodyObjectType)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInterface, bodyObjectType.FullName, typeof(IEnumerable).FullName))); + if (typeof(IXmlSerializable).IsAssignableFrom(bodyObjectType)) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxBodyObjectTypeCannotBeInterface, bodyObjectType.FullName, typeof(IXmlSerializable).FullName))); + + return bodyPart; + } + + private MessagePartDescriptionCollection GetWrappedParts(MessagePartDescription bodyPart) + { + Type bodyObjectType = bodyPart.Type; + MessagePartDescriptionCollection partList = new MessagePartDescriptionCollection(); + foreach (MemberInfo member in bodyObjectType.GetMembers(BindingFlags.Instance | BindingFlags.Public)) + { + if ((member.MemberType & (MemberTypes.Field | MemberTypes.Property)) == 0) + continue; + if (member.IsDefined(typeof(SoapIgnoreAttribute), false/*inherit*/)) + continue; + + XmlName xmlName = new XmlName(member.Name); + MessagePartDescription part = new MessagePartDescription(xmlName.EncodedName, string.Empty); + part.AdditionalAttributesProvider = part.MemberInfo = member; + part.Index = part.SerializationPosition = partList.Count; + part.Type = (member.MemberType == MemberTypes.Property) ? ((PropertyInfo)member).PropertyType : ((FieldInfo)member).FieldType; + if (bodyPart.HasProtectionLevel) + part.ProtectionLevel = bodyPart.ProtectionLevel; + partList.Add(part); + } + + return partList; + } + private XmlMembersMapping LoadHeadersMapping(MessageDescription message, string mappingKey) { int headerCount = message.Headers.Count; if (headerCount == 0) return null; + if (IsEncoded) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxHeadersAreNotSupportedInEncoded, message.MessageName))); int unknownHeaderCount = 0, headerIndex = 0; XmlReflectionMember[] members = new XmlReflectionMember[headerCount]; @@ -494,7 +573,7 @@ private XmlMembersMapping LoadHeadersMapping(MessageDescription message, string MessageHeaderDescription header = message.Headers[i]; if (!header.IsUnknownHeaderCollection) { - members[headerIndex++] = XmlSerializerHelper.GetXmlReflectionMember(header, false/*isRpc*/, false/*isWrapped*/); + members[headerIndex++] = XmlSerializerHelper.GetXmlReflectionMember(header, false/*isRpc*/, IsEncoded, false/*isWrapped*/); } else { @@ -520,7 +599,7 @@ private XmlMembersMapping LoadHeadersMapping(MessageDescription message, string internal XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, string mappingKey) { string key = mappingKey.StartsWith(":", StringComparison.Ordinal) ? _keyBase + mappingKey : mappingKey; - return _parent._importer.ImportMembersMapping(new XmlName(elementName, true /*isEncoded*/), ns, members, hasWrapperElement, rpc, key); + return _parent._importer.ImportMembersMapping(new XmlName(elementName, true /*isEncoded*/), ns, members, hasWrapperElement, rpc, IsEncoded, key); } internal XmlMembersMapping ImportFaultElement(FaultDescription fault, out XmlQualifiedName elementName) @@ -532,20 +611,20 @@ internal XmlMembersMapping ImportFaultElement(FaultDescription fault, out XmlQua string faultNamespace = fault.Namespace; if (faultElementName == null) { - XmlTypeMapping mapping = _parent._importer.ImportTypeMapping(fault.DetailType); - faultElementName = new XmlName(mapping.ElementName, false /*isEncoded*/); + XmlTypeMapping mapping = _parent._importer.ImportTypeMapping(fault.DetailType, IsEncoded); + faultElementName = new XmlName(mapping.ElementName, IsEncoded); faultNamespace = mapping.Namespace; if (faultElementName == null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxFaultTypeAnonymous, this.Operation.Name, fault.DetailType.FullName))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxFaultTypeAnonymous, Operation.Name, fault.DetailType.FullName))); } elementName = new XmlQualifiedName(faultElementName.DecodedName, faultNamespace); members[0] = XmlSerializerHelper.GetXmlReflectionMember(null /*memberName*/, faultElementName, faultNamespace, fault.DetailType, - null /*additionalAttributesProvider*/, false /*isMultiple*/, false /*isWrapped*/); + null /*additionalAttributesProvider*/, false /*isMultiple*/, IsEncoded, false /*isWrapped*/); string mappingKey = "fault:" + faultElementName.DecodedName + ":" + faultNamespace; - return ImportMembersMapping(faultElementName.EncodedName, faultNamespace, members, false /*hasWrapperElement*/, this.IsRpc, mappingKey); + return ImportMembersMapping(faultElementName.EncodedName, faultNamespace, members, false /*hasWrapperElement*/, IsRpc, mappingKey); } } @@ -553,6 +632,7 @@ private class XmlSerializerImporter { private readonly string _defaultNs; private XmlReflectionImporter _xmlImporter; + private SoapReflectionImporter _soapImporter; private Dictionary _xmlMappings; private HashSet _includedTypes; @@ -560,6 +640,20 @@ internal XmlSerializerImporter(string defaultNs) { _defaultNs = defaultNs; _xmlImporter = null; + _soapImporter = null; + } + + private SoapReflectionImporter SoapImporter + { + get + { + if (_soapImporter == null) + { + _soapImporter = new SoapReflectionImporter(NamingHelper.CombineUriStrings(_defaultNs, "encoded")); + } + + return _soapImporter; + } } private XmlReflectionImporter XmlImporter @@ -598,7 +692,7 @@ private HashSet IncludedTypes } } - internal XmlMembersMapping ImportMembersMapping(XmlName elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, string mappingKey) + internal XmlMembersMapping ImportMembersMapping(XmlName elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool isEncoded, string mappingKey) { XmlMembersMapping mapping; string mappingName = elementName.DecodedName; @@ -607,18 +701,25 @@ internal XmlMembersMapping ImportMembersMapping(XmlName elementName, string ns, return mapping; } - mapping = this.XmlImporter.ImportMembersMapping(mappingName, ns, members, hasWrapperElement, rpc); + if (isEncoded) + mapping = SoapImporter.ImportMembersMapping(mappingName, ns, members, hasWrapperElement, rpc); + else + mapping = XmlImporter.ImportMembersMapping(mappingName, ns, members, hasWrapperElement, rpc); + mapping.SetKey(mappingKey); XmlMappings.Add(mappingKey, mapping); return mapping; } - internal XmlTypeMapping ImportTypeMapping(Type type) + internal XmlTypeMapping ImportTypeMapping(Type type, bool isEncoded) { - return this.XmlImporter.ImportTypeMapping(type); + if (isEncoded) + return SoapImporter.ImportTypeMapping(type); + else + return XmlImporter.ImportTypeMapping(type); } - internal void IncludeType(Type knownType) + internal void IncludeType(Type knownType, bool isEncoded) { // XmlReflectionImporter.IncludeTypes calls XmlReflectionImporter.ImportTypeMapping to generate mappings // for types and store those mappings. @@ -633,7 +734,11 @@ internal void IncludeType(Type knownType) if (IncludedTypes.Contains(knownType)) return; - this.XmlImporter.IncludeType(knownType); + if (isEncoded) + SoapImporter.IncludeType(knownType); + else + XmlImporter.IncludeType(knownType); + IncludedTypes.Add(knownType); } } @@ -852,19 +957,22 @@ internal void SetUnknownHeaderDescription(MessageHeaderDescription unknownHeader internal static class XmlSerializerHelper { - static internal XmlReflectionMember GetXmlReflectionMember(MessagePartDescription part, bool isRpc, bool isWrapped) + static internal XmlReflectionMember GetXmlReflectionMember(MessagePartDescription part, bool isRpc, bool isEncoded, bool isWrapped) { string ns = isRpc ? null : part.Namespace; - MemberInfo additionalAttributesProvider = null; - if (part.AdditionalAttributesProvider.MemberInfo != null) + ICustomAttributeProvider additionalAttributesProvider = null; + if (isEncoded || part.AdditionalAttributesProvider.MemberInfo != null) additionalAttributesProvider = part.AdditionalAttributesProvider.MemberInfo; XmlName memberName = string.IsNullOrEmpty(part.UniquePartName) ? null : new XmlName(part.UniquePartName, true /*isEncoded*/); XmlName elementName = part.XmlName; - return GetXmlReflectionMember(memberName, elementName, ns, part.Type, additionalAttributesProvider, part.Multiple, isWrapped); + return GetXmlReflectionMember(memberName, elementName, ns, part.Type, additionalAttributesProvider, part.Multiple, isEncoded, isWrapped); } - static internal XmlReflectionMember GetXmlReflectionMember(XmlName memberName, XmlName elementName, string ns, Type type, MemberInfo additionalAttributesProvider, bool isMultiple, bool isWrapped) + static internal XmlReflectionMember GetXmlReflectionMember(XmlName memberName, XmlName elementName, string ns, Type type, ICustomAttributeProvider additionalAttributesProvider, bool isMultiple, bool isEncoded, bool isWrapped) { + if (isEncoded && isMultiple) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxMultiplePartsNotAllowedInEncoded, elementName.DecodedName, ns))); + XmlReflectionMember member = new XmlReflectionMember(); member.MemberName = (memberName ?? elementName).DecodedName; member.MemberType = type; @@ -872,79 +980,102 @@ static internal XmlReflectionMember GetXmlReflectionMember(XmlName memberName, X member.MemberType = member.MemberType.GetElementType(); if (isMultiple) member.MemberType = member.MemberType.MakeArrayType(); - if (additionalAttributesProvider != null) - { -#if NETStandard13 - member.XmlAttributes = XmlAttributesHelper.CreateXmlAttributes(additionalAttributesProvider); -#else - member.XmlAttributes = new XmlAttributes(additionalAttributesProvider); -#endif - } - if (member.XmlAttributes == null) - member.XmlAttributes = new XmlAttributes(); - else + if (additionalAttributesProvider != null) { - Type invalidAttributeType = null; - if (member.XmlAttributes.XmlAttribute != null) - invalidAttributeType = typeof(XmlAttributeAttribute); - else if (member.XmlAttributes.XmlAnyAttribute != null && !isWrapped) - invalidAttributeType = typeof(XmlAnyAttributeAttribute); - else if (member.XmlAttributes.XmlChoiceIdentifier != null) - invalidAttributeType = typeof(XmlChoiceIdentifierAttribute); - else if (member.XmlAttributes.XmlIgnore) - invalidAttributeType = typeof(XmlIgnoreAttribute); - else if (member.XmlAttributes.Xmlns) - invalidAttributeType = typeof(XmlNamespaceDeclarationsAttribute); - else if (member.XmlAttributes.XmlText != null) - invalidAttributeType = typeof(XmlTextAttribute); - else if (member.XmlAttributes.XmlEnum != null) - invalidAttributeType = typeof(XmlEnumAttribute); - if (invalidAttributeType != null) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(isWrapped ? SR.SFxInvalidXmlAttributeInWrapped : SR.SFxInvalidXmlAttributeInBare, invalidAttributeType, elementName.DecodedName))); - if (member.XmlAttributes.XmlArray != null && isMultiple) - throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxXmlArrayNotAllowedForMultiple, elementName.DecodedName, ns))); + if (isEncoded) + member.SoapAttributes = new SoapAttributes(additionalAttributesProvider); + else + member.XmlAttributes = new XmlAttributes(additionalAttributesProvider); } - - bool isArray = member.MemberType.IsArray; - if ((isArray && !isMultiple && member.MemberType != typeof(byte[])) || - (!isArray && typeof(IEnumerable).IsAssignableFrom(member.MemberType) && member.MemberType != typeof(string) && !typeof(XmlNode).IsAssignableFrom(member.MemberType) && !typeof(IXmlSerializable).IsAssignableFrom(member.MemberType))) + if (isEncoded) { - if (member.XmlAttributes.XmlArray != null) - { - if (member.XmlAttributes.XmlArray.ElementName == String.Empty) - member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; - if (member.XmlAttributes.XmlArray.Namespace == null) - member.XmlAttributes.XmlArray.Namespace = ns; - } - else if (HasNoXmlParameterAttributes(member.XmlAttributes)) + if (member.SoapAttributes == null) + member.SoapAttributes = new SoapAttributes(); + else { - member.XmlAttributes.XmlArray = new XmlArrayAttribute(); - member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; - member.XmlAttributes.XmlArray.Namespace = ns; + Type invalidAttributeType = null; + if (member.SoapAttributes.SoapAttribute != null) + invalidAttributeType = typeof(SoapAttributeAttribute); + else if (member.SoapAttributes.SoapIgnore) + invalidAttributeType = typeof(SoapIgnoreAttribute); + else if (member.SoapAttributes.SoapType != null) + invalidAttributeType = typeof(SoapTypeAttribute); + if (invalidAttributeType != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxInvalidSoapAttribute, invalidAttributeType, elementName.DecodedName))); } + + if (member.SoapAttributes.SoapElement == null) + member.SoapAttributes.SoapElement = new SoapElementAttribute(elementName.DecodedName); } else { - if (member.XmlAttributes.XmlElements == null || member.XmlAttributes.XmlElements.Count == 0) + if (member.XmlAttributes == null) + member.XmlAttributes = new XmlAttributes(); + else { - if (HasNoXmlParameterAttributes(member.XmlAttributes)) + Type invalidAttributeType = null; + if (member.XmlAttributes.XmlAttribute != null) + invalidAttributeType = typeof(XmlAttributeAttribute); + else if (member.XmlAttributes.XmlAnyAttribute != null && !isWrapped) + invalidAttributeType = typeof(XmlAnyAttributeAttribute); + else if (member.XmlAttributes.XmlChoiceIdentifier != null) + invalidAttributeType = typeof(XmlChoiceIdentifierAttribute); + else if (member.XmlAttributes.XmlIgnore) + invalidAttributeType = typeof(XmlIgnoreAttribute); + else if (member.XmlAttributes.Xmlns) + invalidAttributeType = typeof(XmlNamespaceDeclarationsAttribute); + else if (member.XmlAttributes.XmlText != null) + invalidAttributeType = typeof(XmlTextAttribute); + else if (member.XmlAttributes.XmlEnum != null) + invalidAttributeType = typeof(XmlEnumAttribute); + if (invalidAttributeType != null) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(isWrapped ? SR.SFxInvalidXmlAttributeInWrapped : SR.SFxInvalidXmlAttributeInBare, invalidAttributeType, elementName.DecodedName))); + if (member.XmlAttributes.XmlArray != null && isMultiple) + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.SFxXmlArrayNotAllowedForMultiple, elementName.DecodedName, ns))); + } + + + bool isArray = member.MemberType.IsArray; + if ((isArray && !isMultiple && member.MemberType != typeof(byte[])) || + (!isArray && typeof(IEnumerable).IsAssignableFrom(member.MemberType) && member.MemberType != typeof(string) && !typeof(XmlNode).IsAssignableFrom(member.MemberType) && !typeof(IXmlSerializable).IsAssignableFrom(member.MemberType))) + { + if (member.XmlAttributes.XmlArray != null) { - XmlElementAttribute elementAttribute = new XmlElementAttribute(); - elementAttribute.ElementName = elementName.DecodedName; - elementAttribute.Namespace = ns; - member.XmlAttributes.XmlElements.Add(elementAttribute); + if (member.XmlAttributes.XmlArray.ElementName == String.Empty) + member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; + if (member.XmlAttributes.XmlArray.Namespace == null) + member.XmlAttributes.XmlArray.Namespace = ns; + } + else if (HasNoXmlParameterAttributes(member.XmlAttributes)) + { + member.XmlAttributes.XmlArray = new XmlArrayAttribute(); + member.XmlAttributes.XmlArray.ElementName = elementName.DecodedName; + member.XmlAttributes.XmlArray.Namespace = ns; } } else { - foreach (XmlElementAttribute elementAttribute in member.XmlAttributes.XmlElements) + if (member.XmlAttributes.XmlElements == null || member.XmlAttributes.XmlElements.Count == 0) { - if (elementAttribute.ElementName == String.Empty) + if (HasNoXmlParameterAttributes(member.XmlAttributes)) + { + XmlElementAttribute elementAttribute = new XmlElementAttribute(); elementAttribute.ElementName = elementName.DecodedName; - if (elementAttribute.Namespace == null) elementAttribute.Namespace = ns; + member.XmlAttributes.XmlElements.Add(elementAttribute); + } + } + else + { + foreach (XmlElementAttribute elementAttribute in member.XmlAttributes.XmlElements) + { + if (elementAttribute.ElementName == String.Empty) + elementAttribute.ElementName = elementName.DecodedName; + if (elementAttribute.Namespace == null) + elementAttribute.Namespace = ns; + } } } } diff --git a/src/System.Private.ServiceModel/tests/Common/Scenarios/Endpoints.cs b/src/System.Private.ServiceModel/tests/Common/Scenarios/Endpoints.cs index 6c2897eaa3f..d4439e49749 100644 --- a/src/System.Private.ServiceModel/tests/Common/Scenarios/Endpoints.cs +++ b/src/System.Private.ServiceModel/tests/Common/Scenarios/Endpoints.cs @@ -33,6 +33,11 @@ public static string HttpBaseAddress_4_4_0_Basic { get { return GetEndpointAddress("BasicHttp_4_4_0.svc//Basic"); } } + + public static string HttpBaseAddress_Basic_Soap + { + get { return GetEndpointAddress("BasicHttpSoap.svc//Basic"); } + } public static string HttpBaseAddress_NetHttp { diff --git a/src/System.Private.ServiceModel/tests/Common/Scenarios/ScenarioTestTypes.cs b/src/System.Private.ServiceModel/tests/Common/Scenarios/ScenarioTestTypes.cs index fef9d644507..449e926c222 100644 --- a/src/System.Private.ServiceModel/tests/Common/Scenarios/ScenarioTestTypes.cs +++ b/src/System.Private.ServiceModel/tests/Common/Scenarios/ScenarioTestTypes.cs @@ -1213,6 +1213,41 @@ public int Id } } +public class SoapComplexType +{ + private bool _boolValue; + private string _stringValue; + + public bool BoolValue + { + get { return _boolValue; } + set { _boolValue = value; } + } + + public string StringValue + { + get { return _stringValue; } + set { _stringValue = value; } + } +} + +[SoapType(Namespace = "WcfService")] +public class CustomerObject +{ + public string Name { get; set; } + public object Data { get; set; } +} + +[Serializable] +[SoapType(Namespace = "WcfService")] +public partial class AdditionalData +{ + public string Field + { + get; set; + } +} + // This type should be used by XmlSerializerFormat_EchoVeryComplexType only. // The type should not ever be instantiated. public class NonInstantiatedType diff --git a/src/System.Private.ServiceModel/tests/Common/Scenarios/ServiceInterfaces.cs b/src/System.Private.ServiceModel/tests/Common/Scenarios/ServiceInterfaces.cs index c5e6f4779dd..334fcf55da9 100644 --- a/src/System.Private.ServiceModel/tests/Common/Scenarios/ServiceInterfaces.cs +++ b/src/System.Private.ServiceModel/tests/Common/Scenarios/ServiceInterfaces.cs @@ -120,6 +120,24 @@ public interface IWcfServiceXmlGenerated XmlVeryComplexType EchoXmlVeryComplexType(XmlVeryComplexType complex); } +[ServiceContract(ConfigurationName = "IWcfSoapService")] +public interface IWcfSoapService +{ + [OperationContract(Action = "http://tempuri.org/IWcfService/CombineStringXmlSerializerFormatSoap", ReplyAction = "http://tempuri.org/IWcfService/CombineStringXmlSerializerFormatSoapResponse")] + [XmlSerializerFormat(Style = OperationFormatStyle.Rpc, SupportFaults = true, Use = OperationFormatUse.Encoded)] + string CombineStringXmlSerializerFormatSoap(string message1, string message2); + + [OperationContract(Action = "http://tempuri.org/IWcfService/EchoComositeTypeXmlSerializerFormatSoap", ReplyAction = "http://tempuri.org/IWcfService/EchoComositeTypeXmlSerializerFormatSoapResponse")] + [XmlSerializerFormat(Style = OperationFormatStyle.Rpc, SupportFaults = true, Use = OperationFormatUse.Encoded)] + SoapComplexType EchoComositeTypeXmlSerializerFormatSoap(SoapComplexType c); + + [OperationContract(Action = "http://tempuri.org/IWcfService/ProcessCustomerData", ReplyAction = "http://tempuri.org/IWcfSoapService/ProcessCustomerDataResponse")] + [XmlSerializerFormat(Style = OperationFormatStyle.Rpc, SupportFaults = true, Use = OperationFormatUse.Encoded)] + [ServiceKnownType(typeof(AdditionalData))] + [return: MessageParameter(Name = "ProcessCustomerDataReturn")] + string ProcessCustomerData(CustomerObject CustomerData); +} + // This type share the same name space with IWcfServiceXmlGenerated. // And this type contains a method which is also defined in IWcfServiceXmlGenerated. [ServiceContract(ConfigurationName = "IWcfService")] diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Contract/XmlSerializer/XmlSerializerFormatSoapTest.cs b/src/System.Private.ServiceModel/tests/Scenarios/Contract/XmlSerializer/XmlSerializerFormatSoapTest.cs new file mode 100644 index 00000000000..dfd7ee241b5 --- /dev/null +++ b/src/System.Private.ServiceModel/tests/Scenarios/Contract/XmlSerializer/XmlSerializerFormatSoapTest.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.Threading.Tasks; +using Infrastructure.Common; +using Xunit; +using System; + +public static partial class XmlSerializerFormatTests +{ + [WcfFact] + [OuterLoop] + public static void CombineString_XmlSerializerFormat_Soap() + { + RunWcfSoapServiceTest((serviceProxy) => + { + // *** EXECUTE *** \\ + string message1 = "hello"; + string message2 = "world"; + var response = serviceProxy.CombineStringXmlSerializerFormatSoap(message1, message2); + + // *** VALIDATE *** \\ + Assert.Equal(message1 + message2, response); + }); + } + + [WcfFact] + [OuterLoop] + [Issue(1884)] + public static void EchoComositeType_XmlSerializerFormat_Soap() + { + RunWcfSoapServiceTest((serviceProxy) => + { + // *** EXECUTE *** \\ + var value = new SoapComplexType() { BoolValue = true, StringValue = "hello" }; + SoapComplexType response = serviceProxy.EchoComositeTypeXmlSerializerFormatSoap(value); + + // *** VALIDATE *** \\ + Assert.NotNull(response); + Assert.Equal(value.BoolValue, response.BoolValue); + Assert.Equal(value.StringValue, response.StringValue); + }); + } + + [WcfFact] + [OuterLoop] + public static void ProcessCustomerData_XmlSerializerFormat_Soap() + { + RunWcfSoapServiceTest((serviceProxy) => + { + // *** EXECUTE *** \\ + CustomerObject value = new CustomerObject() { Name = "MyName", Data = new AdditionalData() { Field = "Foo" } }; + string response = serviceProxy.ProcessCustomerData(value); + + // *** VALIDATE *** \\ + Assert.Equal("MyNameFoo", response); + }); + } + + private static void RunWcfSoapServiceTest(Action testMethod) + { + BasicHttpBinding binding; + EndpointAddress endpointAddress; + ChannelFactory factory; + IWcfSoapService serviceProxy = null; + + try + { + // *** SETUP *** \\ + binding = new BasicHttpBinding(); + endpointAddress = new EndpointAddress(Endpoints.HttpBaseAddress_Basic_Soap); + factory = new ChannelFactory(binding, endpointAddress); + serviceProxy = factory.CreateChannel(); + testMethod(serviceProxy); + + // *** CLEANUP *** \\ + factory.Close(); + ((ICommunicationObject)serviceProxy).Close(); + } + finally + { + // *** ENSURE CLEANUP *** \\ + ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy); + } + } +} diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/CompositeType.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/CompositeType.cs index 97636c77c48..34e52c77bf6 100644 --- a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/CompositeType.cs +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/CompositeType.cs @@ -315,4 +315,39 @@ public override bool TryResolveType(Type type, Type declaredType, DataContractRe return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace); } } -} \ No newline at end of file +} + +public class SoapComplexType +{ + private bool _boolValue; + private string _stringValue; + + public bool BoolValue + { + get { return _boolValue; } + set { _boolValue = value; } + } + + public string StringValue + { + get { return _stringValue; } + set { _stringValue = value; } + } +} + +[SoapType(Namespace = "WcfService")] +public class CustomerObject +{ + public string Name { get; set; } + public object Data { get; set; } +} + +[Serializable] +[SoapType(Namespace = "WcfService")] +public partial class AdditionalData +{ + public string Field + { + get; set; + } +} diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/IWcfSoapService.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/IWcfSoapService.cs new file mode 100644 index 00000000000..b0cefcd2f2c --- /dev/null +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/IWcfSoapService.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.ServiceModel; +using System.Threading.Tasks; + +namespace WcfService +{ + [ServiceContract] + [XmlSerializerFormat(Use = OperationFormatUse.Encoded)] + public interface IWcfSoapService + { + [OperationContract(Action = "http://tempuri.org/IWcfService/CombineStringXmlSerializerFormatSoap")] + [XmlSerializerFormat(Use = OperationFormatUse.Encoded)] + string CombineStringXmlSerializerFormatSoap(string message1, string message2); + + [OperationContract(Action = "http://tempuri.org/IWcfService/EchoComositeTypeXmlSerializerFormatSoap")] + [XmlSerializerFormat(Use = OperationFormatUse.Encoded)] + SoapComplexType EchoComositeTypeXmlSerializerFormatSoap(SoapComplexType c); + + [OperationContract(Action = "http://tempuri.org/IWcfService/ProcessCustomerData")] + [XmlSerializerFormat(Style = OperationFormatStyle.Rpc, SupportFaults = true, Use = OperationFormatUse.Encoded)] + [ServiceKnownType(typeof(AdditionalData))] + [return: MessageParameter(Name = "ProcessCustomerDataReturn")] + [return: System.Xml.Serialization.SoapElement(DataType = "string")] + string ProcessCustomerData([MessageParameter(Name = "CustomerData")]CustomerObject customerData); + } +} diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/WcfSoapService.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/WcfSoapService.cs new file mode 100644 index 00000000000..20c5d9a4030 --- /dev/null +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/WcfSoapService.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.ServiceModel; +using System.ServiceModel.Channels; +using System.ServiceModel.Web; +using System.Text; +using System.Xml.Serialization; + +namespace WcfService +{ + public class WcfSoapService : IWcfSoapService + { + public string CombineStringXmlSerializerFormatSoap(string message1, string message2) + { + return message1 + message2; + } + + public SoapComplexType EchoComositeTypeXmlSerializerFormatSoap(SoapComplexType complexObject) + { + return complexObject; + } + + [return: MessageParameter(Name = "ProcessCustomerDataReturn"), SoapElement(DataType = "string")] + public string ProcessCustomerData([MessageParameter(Name = "CustomerData")] CustomerObject customerData) + { + return customerData.Name + ((AdditionalData)customerData.Data).Field; + } + } +} diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpSoapTestServiceHost.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpSoapTestServiceHost.cs new file mode 100644 index 00000000000..cfe47104bb1 --- /dev/null +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpSoapTestServiceHost.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ServiceModel; +using System.ServiceModel.Activation; +using System.ServiceModel.Channels; + +namespace WcfService +{ + public class BasicHttpSoapTestServiceHostFactory : ServiceHostFactory + { + protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) + { + var serviceHost = new BasicHttpSoapTestServiceHost(serviceType, baseAddresses); + return serviceHost; + } + } + + public class BasicHttpSoapTestServiceHost : TestServiceHostBase + { + protected override string Address { get { return "Basic"; } } + + protected override Binding GetBinding() + { + return new BasicHttpBinding(); + } + + public BasicHttpSoapTestServiceHost(Type serviceType, params Uri[] baseAddresses) + : base(serviceType, baseAddresses) + { + } + } +} diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/Web.config b/src/System.Private.ServiceModel/tools/IISHostedWcfService/Web.config index 4f55486fe4e..c9893e33196 100644 --- a/src/System.Private.ServiceModel/tools/IISHostedWcfService/Web.config +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/Web.config @@ -26,6 +26,7 @@ + diff --git a/src/System.Private.ServiceModel/tools/SelfHostedWcfService/Program.cs b/src/System.Private.ServiceModel/tools/SelfHostedWcfService/Program.cs index 497932598cb..6de09e81151 100644 --- a/src/System.Private.ServiceModel/tools/SelfHostedWcfService/Program.cs +++ b/src/System.Private.ServiceModel/tools/SelfHostedWcfService/Program.cs @@ -42,7 +42,8 @@ private static void Main() CreateHost("BasicAuth.svc", httpsBaseAddress); CreateHost("BasicHttps.svc", httpsBaseAddress); CreateHost("BasicHttp.svc", httpBaseAddress); - CreateHost("BasicHttp_4_4_0.svc", httpBaseAddress); + CreateHost("BasicHttp_4_4_0.svc", httpBaseAddress); + CreateHost("BasicHttpSoap.svc", httpBaseAddress); CreateHost("CustomTextEncoderBuffered.svc", httpBaseAddress); CreateHost("CustomTextEncoderStreamed.svc", httpBaseAddress); CreateHost("DefaultCustomHttp.svc", httpBaseAddress); diff --git a/src/System.ServiceModel.Primitives/ref/System.ServiceModel.Primitives.cs b/src/System.ServiceModel.Primitives/ref/System.ServiceModel.Primitives.cs index 4a291bb8184..599593a17c6 100644 --- a/src/System.ServiceModel.Primitives/ref/System.ServiceModel.Primitives.cs +++ b/src/System.ServiceModel.Primitives/ref/System.ServiceModel.Primitives.cs @@ -1453,6 +1453,7 @@ public partial class XmlSerializerOperationBehavior : System.ServiceModel.Descri public XmlSerializerOperationBehavior(System.ServiceModel.Description.OperationDescription operation) { } public XmlSerializerOperationBehavior(System.ServiceModel.Description.OperationDescription operation, System.ServiceModel.XmlSerializerFormatAttribute attribute) { } public System.ServiceModel.XmlSerializerFormatAttribute XmlSerializerFormatAttribute { get { return default(System.ServiceModel.XmlSerializerFormatAttribute); } } + public System.Collections.ObjectModel.Collection GetXmlMappings() { throw null; } void System.ServiceModel.Description.IOperationBehavior.Validate(System.ServiceModel.Description.OperationDescription description) { } void System.ServiceModel.Description.IOperationBehavior.AddBindingParameters(System.ServiceModel.Description.OperationDescription description, System.ServiceModel.Channels.BindingParameterCollection parameters) { } void System.ServiceModel.Description.IOperationBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.OperationDescription description, System.ServiceModel.Dispatcher.DispatchOperation dispatch) { }