diff --git a/src/SoapCore/Meta/BodyWriterExtensions.cs b/src/SoapCore/Meta/BodyWriterExtensions.cs
index fc5232b3..1ba7f841 100644
--- a/src/SoapCore/Meta/BodyWriterExtensions.cs
+++ b/src/SoapCore/Meta/BodyWriterExtensions.cs
@@ -160,6 +160,18 @@ public static bool IsIgnored(this MemberInfo member)
attr.AttributeType == typeof(XmlIgnoreAttribute));
}
+ public static bool IsIgnored(this MemberInfo member, bool xmlIgnoreOnly)
+ {
+ if (xmlIgnoreOnly)
+ {
+ return member
+ .CustomAttributes
+ .Any(attr => attr.AttributeType == typeof(XmlIgnoreAttribute));
+ }
+
+ return member.IsIgnored();
+ }
+
///
/// Checks if the parent has a ShouldSerialize*() method defined for a specific member.
///
diff --git a/src/SoapCore/Meta/ClrTypeResolver.cs b/src/SoapCore/Meta/ClrTypeResolver.cs
index bb2a574a..fe1bff50 100644
--- a/src/SoapCore/Meta/ClrTypeResolver.cs
+++ b/src/SoapCore/Meta/ClrTypeResolver.cs
@@ -1,7 +1,16 @@
+using System;
+
namespace SoapCore.Meta
{
public class ClrTypeResolver
{
+ ///
+ /// When true, Guid types are resolved as "guid" (for Microsoft WSDL types namespace)
+ /// instead of "string". This affects array naming (List<Guid> becomes ArrayOfGuid).
+ ///
+ [ThreadStatic]
+ public static bool UseMicrosoftGuid;
+
public static string ResolveOrDefault(string typeName)
{
switch (typeName)
@@ -33,7 +42,9 @@ public static string ResolveOrDefault(string typeName)
case "DateTime":
return "dateTime";
case "Guid":
- return "string";
+ // When UseMicrosoftGuid is true, return null so callers fall back to original type name "Guid"
+ // This ensures ArrayOfGuid naming while avoiding invalid "s:guid" type references
+ return UseMicrosoftGuid ? null : "string";
case "Char":
return "string";
case "TimeSpan":
diff --git a/src/SoapCore/Meta/IWsdlOperationNameGenerator.cs b/src/SoapCore/Meta/IWsdlOperationNameGenerator.cs
index 08b2f759..984763b0 100644
--- a/src/SoapCore/Meta/IWsdlOperationNameGenerator.cs
+++ b/src/SoapCore/Meta/IWsdlOperationNameGenerator.cs
@@ -20,4 +20,17 @@ public string GenerateWsdlOutputMessageName(OperationDescription operation, Serv
return $"{service.GeneralContract.Name}_{operation.Name}_OutputMessage";
}
}
+
+ public class LegacyWsdlOperationNameGenerator : IWsdlOperationNameGenerator
+ {
+ public string GenerateWsdlInputMessageName(OperationDescription operation, ServiceDescription service)
+ {
+ return $"{operation.Name}SoapIn";
+ }
+
+ public string GenerateWsdlOutputMessageName(OperationDescription operation, ServiceDescription service)
+ {
+ return $"{operation.Name}SoapOut";
+ }
+ }
}
diff --git a/src/SoapCore/Meta/MetaBodyWriter.cs b/src/SoapCore/Meta/MetaBodyWriter.cs
index caa980b1..2a52e445 100644
--- a/src/SoapCore/Meta/MetaBodyWriter.cs
+++ b/src/SoapCore/Meta/MetaBodyWriter.cs
@@ -34,9 +34,11 @@ public class MetaBodyWriter : BodyWriter
private readonly Dictionary> _requestedDynamicTypes;
private readonly bool _buildMicrosoftGuid = false;
+ private readonly bool _xmlIgnoreOnlyForWsdl = false;
private IWsdlOperationNameGenerator _wsdlOperationNameGenerator;
+ private readonly string _portTypeSuffix;
- public MetaBodyWriter(ServiceDescription service, string baseUrl, ConcurrentXmlNamespaceLookup xmlNamespaceLookup, string bindingName, SoapBindingInfo[] soapBindings, bool buildMicrosoftGuid, IWsdlOperationNameGenerator wsdlOperationNameGenerator) : base(isBuffered: true)
+ public MetaBodyWriter(ServiceDescription service, string baseUrl, ConcurrentXmlNamespaceLookup xmlNamespaceLookup, string bindingName, SoapBindingInfo[] soapBindings, bool buildMicrosoftGuid, IWsdlOperationNameGenerator wsdlOperationNameGenerator, bool xmlIgnoreOnlyForWsdl = false, string portTypeSuffix = "") : base(isBuffered: true)
{
_service = service;
_baseUrl = baseUrl;
@@ -52,12 +54,14 @@ public MetaBodyWriter(ServiceDescription service, string baseUrl, ConcurrentXmlN
PortName = bindingName;
SoapBindings = soapBindings;
_buildMicrosoftGuid = buildMicrosoftGuid;
+ _xmlIgnoreOnlyForWsdl = xmlIgnoreOnlyForWsdl;
_wsdlOperationNameGenerator = wsdlOperationNameGenerator;
+ _portTypeSuffix = portTypeSuffix ?? "";
}
private SoapBindingInfo[] SoapBindings { get; }
private string BindingName { get; }
- private string BindingType => _service.GeneralContract.Name;
+ private string BindingType => _service.GeneralContract.Name + _portTypeSuffix;
private string PortName { get; }
private string TargetNameSpace => _service.GeneralContract.Namespace;
@@ -215,6 +219,13 @@ private static bool TryGetMessageContractBodyMemberInfo(Type type, out MemberInf
private XmlQualifiedName ResolveType(Type type)
{
string typeName = type.IsEnum ? type.GetEnumUnderlyingType().Name : type.Name;
+
+ // Handle Guid specially when UseMicrosoftGuid is enabled
+ if (typeName == "Guid" && _buildMicrosoftGuid)
+ {
+ return new XmlQualifiedName("guid", Namespaces.MICROSOFT_TYPES);
+ }
+
string resolvedType = ClrTypeResolver.ResolveOrDefault(typeName);
if (string.IsNullOrEmpty(resolvedType))
@@ -856,7 +867,7 @@ private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuil
if (!isWrappedBodyType)
{
var propertyOrFieldMembers = toBuildBodyType.GetPropertyOrFieldMembers()
- .Where(mi => !mi.IsIgnored() && mi.DeclaringType == toBuildType)
+ .Where(mi => !mi.IsIgnored(_xmlIgnoreOnlyForWsdl) && mi.DeclaringType == toBuildType)
.ToList();
var elements = propertyOrFieldMembers.Where(t => !t.IsAttribute() && t.GetCustomAttribute() == null).ToList();
@@ -887,7 +898,7 @@ private void AddSchemaComplexType(XmlDictionaryWriter writer, TypeToBuild toBuil
else
{
// TODO: should this also be changed to GetPropertyOrFieldMembers?
- var properties = toBuildType.GetProperties().Where(prop => !prop.IsIgnored())
+ var properties = toBuildType.GetProperties().Where(prop => !prop.IsIgnored(_xmlIgnoreOnlyForWsdl))
.ToList();
var elements = properties.Where(t => !t.IsAttribute()).ToList();
diff --git a/src/SoapCore/SoapCoreOptions.cs b/src/SoapCore/SoapCoreOptions.cs
index 388f8106..148fd37a 100644
--- a/src/SoapCore/SoapCoreOptions.cs
+++ b/src/SoapCore/SoapCoreOptions.cs
@@ -148,6 +148,22 @@ public class SoapCoreOptions
///
public string SchemeOverride { get; set; }
+ ///
+ /// When true, only [XmlIgnore] is used to exclude properties from WSDL generation,
+ /// ignoring [IgnoreDataMember]. This matches legacy WCF/ASMX XmlSerializer behavior
+ /// where [IgnoreDataMember] had no effect on XmlSerializer.
+ /// Defaults to false
+ ///
+ public bool XmlIgnoreOnlyForWsdl { get; set; } = false;
+
+ ///
+ /// When true, uses legacy WCF/ASMX WSDL naming conventions:
+ /// portType name becomes {ContractName}Soap, and message names
+ /// use {OperationName}SoapIn/{OperationName}SoapOut pattern.
+ /// Defaults to false
+ ///
+ public bool UseLegacyWsdlNaming { get; set; } = false;
+
public void UseCustomSerializer()
where TCustomSerializer : class, IXmlSerializationHandler
{
diff --git a/src/SoapCore/SoapEndpointMiddleware.cs b/src/SoapCore/SoapEndpointMiddleware.cs
index 69f98e7b..e495aed0 100644
--- a/src/SoapCore/SoapEndpointMiddleware.cs
+++ b/src/SoapCore/SoapEndpointMiddleware.cs
@@ -234,12 +234,15 @@ private async Task ReadMessageAsync(HttpContext httpContext, SoapMessag
private async Task ProcessMeta(HttpContext httpContext, bool showDocumentation)
{
+ // Set the flag for ClrTypeResolver so that List becomes ArrayOfGuid
+ Meta.ClrTypeResolver.UseMicrosoftGuid = _options.UseMicrosoftGuid;
+
var scheme = string.IsNullOrEmpty(_options.SchemeOverride) ? httpContext.Request.Scheme : _options.SchemeOverride;
var baseUrl = scheme + "://" + httpContext.Request.Host + httpContext.Request.PathBase + httpContext.Request.Path;
var xmlNamespaceLookup = GetXmlNamespaceLookup(null);
var bindingName = !string.IsNullOrWhiteSpace(_options.EncoderOptions[0].BindingName) ? _options.EncoderOptions[0].BindingName : "BasicHttpBinding_" + _service.GeneralContract.Name;
var bodyWriter = _options.SoapSerializer == SoapSerializer.XmlSerializer
- ? new MetaBodyWriter(_service, baseUrl, xmlNamespaceLookup, bindingName, _messageEncoders.Select(me => new SoapBindingInfo(me.MessageVersion, me.BindingName, me.PortName)).ToArray(), _options.UseMicrosoftGuid, _options.WsdlOperationNameGenerator)
+ ? new MetaBodyWriter(_service, baseUrl, xmlNamespaceLookup, bindingName, _messageEncoders.Select(me => new SoapBindingInfo(me.MessageVersion, me.BindingName, me.PortName)).ToArray(), _options.UseMicrosoftGuid, _options.UseLegacyWsdlNaming ? new LegacyWsdlOperationNameGenerator() : _options.WsdlOperationNameGenerator, _options.XmlIgnoreOnlyForWsdl, _options.UseLegacyWsdlNaming ? "Soap" : "")
: (BodyWriter)new MetaWCFBodyWriter(_service, baseUrl, bindingName, _options.UseBasicAuthentication, _messageEncoders.Select(me => new SoapBindingInfo(me.MessageVersion, me.BindingName, me.PortName)).ToArray(), _options.WsdlOperationNameGenerator);
//assumption that you want soap12 if your service supports that
diff --git a/src/SoapCore/SoapOptions.cs b/src/SoapCore/SoapOptions.cs
index 8f8ebf26..6feeb038 100644
--- a/src/SoapCore/SoapOptions.cs
+++ b/src/SoapCore/SoapOptions.cs
@@ -71,6 +71,10 @@ public class SoapOptions
public string SchemeOverride { get; set; }
+ public bool XmlIgnoreOnlyForWsdl { get; set; } = false;
+
+ public bool UseLegacyWsdlNaming { get; set; } = false;
+
[Obsolete]
public static SoapOptions FromSoapCoreOptions(SoapCoreOptions opt)
{
@@ -105,6 +109,8 @@ public static SoapOptions FromSoapCoreOptions(SoapCoreOptions opt, Type serviceT
GenerateSoapActionWithoutContractName = opt.GenerateSoapActionWithoutContractName,
NormalizeNewLines = opt.NormalizeNewLines,
SchemeOverride = opt.SchemeOverride,
+ XmlIgnoreOnlyForWsdl = opt.XmlIgnoreOnlyForWsdl,
+ UseLegacyWsdlNaming = opt.UseLegacyWsdlNaming,
};
return options;