WCF OperationContract nicht optionale Prameter im Wsdl

Wenn man mit der WCF einen Service definiert und dabei Nachrichtenbasiert kommunizieren möchte, sieht kann die Definition z.B. so aussehen:

[ServiceContract(Namespace = APICommon.DefaultSOAPNameSpace)]
public interface ICartSoapService
{
[OperationContract]
ProcessCartResponse ProcessCart(ProcessCartRequest request);
}


[DataContract]
public class ProcessCartRequest
{
[DataMember(IsRequired = true)]
public Cart Cart
{
get;
set;
}
}

[DataContract]
public class ProcessCartResponse : BaseResponseMessage
{
[DataMember(IsRequired = true)]
public ProcessResultType ProcessResult
{
get;
set;
}
}
Die WCF erzeugt auch brav eine passendes Wsdl Datei für diese Beschreibung. Allerdings hat die "Standardausgabe" der WCF datei den Nachteil, das der Parameter "request" der Methode ProcessCart immer optionaler Parameter ist bzw. in der Wsdl Datei wird das Element mit den Attribute minOccurs="0" gekennezichnet. Da ohne den Parameter die Methode aber nicht vernüftig abgearbeitet werden kann, müsste im Wsdl eigentlich ein minOccurs="1" stehen ...also kein optionaler Parameter.

Leider gibt es bei den Standardattributen der WCF keine Möglichkeit, diese Verhalten oder viel mehr die Wsdl-Generierung zu beinflussen. Trotzdem kann man durch ein eigenes Attribute das gewünschte Verhalten sehr schnell der WCF beibringen.

Dafür muss man lediglich ein Attribute anlegen und die Interfaces IContractBehavior + IWsdlExportExtension mit hinzufügen. Die vollständige Implementierung sieht so aus:

    [AttributeUsage(AttributeTargets.Interface)]
public class OperationsParametersAreRequiredAttribute : Attribute,
IContractBehavior, IWsdlExportExtension
{
private List<RequiredOperationParameter> _requiredOperationParameters;

public void AddBindingParameters(
ContractDescription contractDescription,
ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}

public void ApplyClientBehavior(
ContractDescription contractDescription,
ServiceEndpoint endpoint,
System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
}

public void ApplyDispatchBehavior(
ContractDescription contractDescription,
ServiceEndpoint endpoint,
System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
}

public void ExportContract(
WsdlExporter exporter,
WsdlContractConversionContext context)
{
if (_requiredOperationParameters == null)
_requiredOperationParameters = new List<RequiredOperationParameter>();

foreach (var operation in context.Contract.Operations)
{
var requestMessage = operation.Messages.Where(m => m.Direction ==
MessageDirection.Input).FirstOrDefault();
var parameters = operation.SyncMethod.GetParameters();
Debug.Assert(parameters.Length == requestMessage.Body.Parts.Count);

for (var iLoop = 0; iLoop < parameters.Length; iLoop++)
{
var attributes = parameters[iLoop].GetCustomAttributes(
typeof
(OperationParameterIsOptionalAttribute), false);

if (attributes.Length == 0)
{
_requiredOperationParameters.Add(new RequiredOperationParameter
{
Namespace = requestMessage.Body.Parts[iLoop].Namespace,
Name = requestMessage.Body.Parts[iLoop].Name,
Message = operation.Name
});
}
}
}
}

public void ExportEndpoint(
WsdlExporter exporter,
WsdlEndpointConversionContext context)
{
foreach (var requiredParamter in _requiredOperationParameters)
{
var schemas = exporter.GeneratedXmlSchemas.Schemas(requiredParamter.Namespace);
foreach (XmlSchema schema in schemas)
{
var message = schema.Elements[requiredParamter.XmlName] as XmlSchemaElement;
var complexType = message.ElementSchemaType as XmlSchemaComplexType;
var sequence = complexType.Particle as XmlSchemaSequence;

foreach (XmlSchemaElement schemaElement in sequence.Items)
{
if (schemaElement.Name == requiredParamter.Name)
{
schemaElement.MinOccurs = 1;
schemaElement.MinOccursString = "1";
break;
}
}
}
}
_requiredOperationParameters.Clear();
}

public void Validate(
ContractDescription contractDescription,
ServiceEndpoint endpoint)
{
}

internal class RequiredOperationParameter
{
public string Message { get; set; }
public string Name { get; set; }
public string Namespace { get;set;}
public XmlQualifiedName XmlName
{
get { return new XmlQualifiedName(Message, Namespace); }
}
}
}




Kommentar schreiben