Why am I getting an InvalidOperationException after adding a [MessageContract] attribute? - wcf

The operation 'PRPA_IN201301UV02' could not be loaded because it has a parameter or return type of type System.ServiceModel.Channels.Message or a type that has MessageContractAttribute and other parameters of different types. When using System.ServiceModel.Channels.Message or types with MessageContractAttribute, the method must not use any other types of parameters.
I'm running a WCF on a console host, this is the contract:
[MessageContract]
public class opRequest
{
[MessageBodyMember]
public string myProperty;
}
[ServiceContract(Namespace = "urn:hl7-org:v3")]
public interface IHL7v3
{
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02")]
string PIXManager_PRPA_IN201301UV02(opRequest clientID);
}
It does run when I remove from opRequest class the [MessageContract] and [MessageBodyMember]
I'm completely not sure if that will get me to what I need, so i'll give the wider scope - I'm trying to get the SOAP body to be without an enclosing tag of the parameter name.
for example (the body extract from the SOAP message) instead of:
<s:Body>
<PRPA_IN201301UV02 xmlns="urn:hl7-org:v3">
<clientID>the xml document is enclosed</clientID>
</PRPA_IN201301UV02>
I want it to be like this:
<s:Body>
<PRPA_IN201301UV02 xmlns="urn:hl7-org:v3">
my given xml document will go here...
</PRPA_IN201301UV02>
I need it like that to conform to a standard (HL7v3 PIX Manager SOAP Web Service).
Any ideas?

Looks like you should use MessageContract for your return parameter as well
EDITED:
Have a look at this MSDN article for more details Using Message Contracts
If you design your contract with messages you can't use other types either as a parameter or a return value.
Here is a code snippet from the article:
[OperationContract]
bool Validate(BankingTransaction bt);
// Invalid, the return type is not a message contract.
[OperationContract]
void Reconcile(BankingTransaction bt1, BankingTransaction bt2);
// Invalid, there is more than one parameter.

Did you try to use an XMLDocument as the input parameter instead of opRequest? You will also have to mark the interface to use the XML Serializer:
[ServiceContract(Namespace = "urn:hl7-org:v3")]
[XmlSerializerFormat]
public interface IHL7v3
{
[OperationContract(Name = "PRPA_IN201301UV02", Action = "urn:hl7-org:v3:PRPA_IN201301UV02")]
XMLDocument PIXManager_PRPA_IN201301UV02(XMLDocument doc);
}
I am presuming you are also returning XML.
Please note that this is wide open - any XML can be sent which may not be what you intend as there no explicit data contract.

Related

WCF Custom Client Inspector

An external company has given me a WSDL to consume which has a couple of odd characteristics which I don't want to impact my client code.
Firstly, each OperationContract requires the same username parameter sent over. Instead of setting this each time in my client code I'd like to do this globally.
I believe setting this in a IClientMessageInspector is my best bet, however, with this being a SOAP service I'm a little confused at how to add this into the body.
public class CustomInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Add an additional parameter to the SOAP body
return null;
}
}
Secondly, whilst the service does return mapped objects, one of the objects contains an xml document shoved in a CDATA :(
<a:ResponseData>
<![CDATA[ INSERT XML DOCUMENT HERE]]>
</a:ResponseData>
I'm looking to extract the XML out and add it back in without the CDATA and XML declaration so I can add the appropriate properties on my response object. That way it should deserialize like normal(hope that makes sense)
public class CustomInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Get the XML from the ResponseData element and remove the CDATA. Add the XML back in (Minus the <xml> declaration)
}
}
Firstly, each OperationContract requires the same username parameter
sent over. Instead of setting this each time in my client code I'd
like to do this globally. I believe setting this in a
IClientMessageInspector is my best bet, however, with this being a
SOAP service I'm a little confused at how to add this into the body.
If you want to add custom message header to the message, you could refer to the following code.
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("username", "", "user"));
request.Headers.Add(MessageHeader.CreateHeader("password", "", "pass"));
return null;
}
Take a look at IClientMessageInspector.
Here are some links may be useful to you.
Adding custom SOAP headers from Silverlight client
https://weblogs.asp.net/paolopia/handling-custom-soap-headers-via-wcf-behaviors
https://social.msdn.microsoft.com/Forums/vstudio/en-US/f1f29779-0121-4499-a2bc-63ffe8025b21/wcf-security-soap-header

WCF Polymorphism in service contract

I am trying to create a service that with an operation that accepts, as a parameter, an any object that implements a specific interface. I would have thought this would be easy to accomplish, but I am running into problems (what I am guessing to be serialization problems but I am not certain). I have the following contract:
//Unsustainable because I would need a method for each of the (currently)
//3 student types, plus I have 2 more root categories that have multiple subtypes
public interface IEmailTemplateAccess
{
[FaultContract(typeof(ValidationFault))]
[FaultContract(typeof(ErrorResponse))]
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
TemplateResponse GetStudentTemplate(ITemplateRequest request);
}
And this is what I would like it to look like:
public interface IEmailTemplateAccess
{
[FaultContract(typeof(ValidationFault))]
[FaultContract(typeof(ErrorResponse))]
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
TemplateResponse GetTemplate(ITemplateRequest request);
}
In my service I use an abstract factory to return the correct template based on the type of request that comes in. In addition, I have created concrete ITemplateRequests for the different kinds of templates that could be returned. For example, I have Template Request types A and B. Template Request Type A can have one of 3 sub types, SubType1, SubType2 and SubType3. I then created a SubType3 request that implemented the ITemplateRequest interface (SubType3Request).
I would hate to have to create a method for each request type I have (i.e. GetSubType1Template, GetSubType2Template, GetSubType3Template, GetTypeBTemplate, etc) as this would quickly become unwieldy as the types of templates I can get will be changing occasionally.
Is there a way to have a contract method accept anything that implements ITemplateRequest as a parameter and let my factory do the work of figuring out what type of template to get?
So far, I have the following methods in my service:
//Not a part of the contract right now although I would like it to be
public IEmailTemplate GetTemplate(ITemplateRequest request)
{
TemplateFactoryBuilder builder = new TemplateFactoryBuilder();
ITemplateFactory factory = builder.GetTemplateFactory(request.Type);
var template = factory.GetTemplate(request);
return template;
}
//contract method --This would be my Parent Request Type (RequestTypeA) from above.
//There are 3 subtypes of the Student type
public TemplateResponse GetStudentTemplate(StudentEmailTemplateRequest request)
{
var response = new TemplateResponse
{
RequiresProcessing = true
};
response.Template = (EmailMergeTemplate) GetTemplate(request);
return response;
}
Sorry for the link-ish answer, but it's pretty long.. What you're after (I think) is here: http://blogs.msdn.com/b/morgan/archive/2009/08/05/polymorphism-in-wcf.aspx
It boils down to using known types. Something like this;
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(CommandServiceHelper))]
public interface ICommandService

how to set attributes in soap format WCF

how to set attributes for soap message:
for example my soap messg look like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity>
<a:codeField>Payment Request Successful</a:codeField>
<a:textField i:nil="true" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
but i need a soap message which take attributes not elements
like following
<doPaymentResult xmlns:a="http://schemas.datacontract.org/2004/07/MemoService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:errors i:nil="true"/>
<a:messages>
<a:MessageEntity codeField="Payment Request Successful">
some text here
</a:MessageEntity>
</a:messages>
<a:requestTrackCode>20130430T125904R14646</a:requestTrackCode>
<a:status i:nil="true"/>
</doPaymentResult>
</doPaymentResponse>
I am using datacontract in class.
It’s a common problem – you want to return an object from a WCF service as XML, but you either want, or need, to deliver some or all of the property values as XML Attributes instead of XML Elements; but you can’t because the DataContractSerializer doesn’t support attributes (you’re most likely to have seen this StackOverflow QA if you’ve done a web search). Most likely you’ve then migrated all your WCF service code to using the XmlSerializer (with all the XmlElement/XmlAttribute/XmlType attributes et al) – and you’ve cursed loudly.
Well, I’m here to rescue you, because it is possible – and the answer to the problem is actually inferred from the MSDN article entitled ‘Types supported by the Data Contract Serializer’.
The example I’m going to give is purely for illustration purposes only. I don’t have a lot of time, so work with me!
•Create a new Asp.Net WCF service application, you can use Cassini as your web server (probably easier – otherwise you might have to enable Asp.Net compatibility mode).
•Open the web.config and delete the element that was created for the new service.
•The interface and implementation model for this example is overkill. Move the [ServiceContract] and [OperationContract] declarations from the interface that was created for you new service to the class that was also created. Delete the interface.
•Open the .svc markup file and add the following at the end: Factory="System.ServiceModel.Activation.WebServiceHostFactory" – this enables the zero-configuration WCF model for this service (we’re going to create a RESTful service).
•Paste the following class declarations into your svc codebehind:
public interface IExampleData
{
string Description { get; set; }
string Name { get; set; }
int ID { get; set; }
}
public class ExampleData : IExampleData
{
public string Description { get; set; }
public string Name { get; set; }
public int ID { get; set; }
}
public class ExampleDataAttributed : ExampleData, IXmlSerializable
{
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
//implement if remote callers are going to pass your object in
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteAttributeString("id", ID.ToString());
writer.WriteAttributeString("name", Name);
//we'll keep the description as an element as it could be long.
writer.WriteElementString("description", Description);
}
#endregion
}
Just to demonstrate the point, the class that will be part-serialized to attributes simply derives from one that will be serialized as normal.
•Now add the following two methods to your service class:
[OperationContract]
[WebGet(UriTemplate = "/test1")]
public ExampleData Test1()
{
return new ExampleData() { ID = 1,
Name = "Element-centric",
Description =
"The contents of this item are entirely serialized to elements - as normal" };
}
[OperationContract]
[WebGet(UriTemplate = "/test2")]
public ExampleDataAttributed Test2()
{
return new ExampleData_Attributed() { ID = 2,
Name = "Mixed",
Description =
"Everything except this description will be serialized to attributes" };
}
Cover, and bake for 40 minutes (that is – Build it).
If you left your service as Service1.svc, then run it and open up IE and browse to http://localhost:[port of cassini]/test1
The result should look something like this:
<JSLabs.ExampleData
xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Description>
The contents of this item are entirely serialized to elements - as normal
</Description>
<ID>
1
</ID>
<Name>
Element-centric
</Name>
</JSLabs.ExampleData>
Now browse to http://localhost:[port of cassini]/test2
<JSLabs.ExampleDataAttributed id="2" name="Mixed"
xmlns="http://schemas.datacontract.org/2004/07/JobServe.Labs.Web">
<description>Everything except this description will be
serialized to attributes</description>
</JSLabs.ExampleDataAttributed>
It’s made a little less impressive by that nasty ‘orrible “xmlns=” attribute that the WCF data contract serializer automatically puts on the type – but, as you can see, the ‘ID’ and ‘Name’ properties have indeed been pushed out as attributes!
We could have made both methods return IExampleData and then used the KnownType attribute on that interface in order to get it to support either (according to what the code of the methods returned).
To support deserializing an object from the attributes, all you have to do is to implement the IXmlSerializable.ReadXml method.
Finally, as the aforementioned MSDN article says about the supported types – you should also be able to use XmlElement/XmlNode types as a way of representing XML directly – the DataContractSerializer, like in this case, take the short route and simply gets the Xml.
This also shouldn’t affect JSON formatting if you’re dual-outputting objects for either XML or JSON clients.
Check the source of this article

A data contract that simply takes a string

Newbie in WCF, I am to define a restful interface for taking in requests in xml. The xml can belongs to any type of schema, instead of defining a data contract describing every data member, I want to my data contract to a simple string, which basicaly says you can send me anything, ideally, it looks below,
[OperationContract]
void SubmitESBMessage(string data);
Tried to define the contract to be
[DataContract]
public class OnRampData
{
[DataMember]
public string Data { get; set; }
}
But I don't want to tell my users to escape their xml and put into the OnRampData.
Anyone please help. Thanks in advance.
If you want to take any XML as your input, then you can use XmlElement or XElement as the parameter type. WCF treats those types as "all the XML from the request body".
[OperationContract]
void SubmitESBMessage(XElement data);

WCF 4: Passing Empty parameters on a GET request

I'm creating an API which will just use a get request to return some search results from the database, I'm trying to make it so that optional parameters can be passed (easy with WCF) but also so that if parameters are specfied in the query string as long as they are empty they will be ignored by the service.
However if you have the a query string with empty parameters it will return a bad request (400) by the server e.g.
Using a end-user point of your choice pass the following querystring
http://www.exampleservice.com/basic/?apiKey=1234&noOfResults=3&maxSalary=&minSalary=&ouId=0&keywords=Web+Developer
Note that maxSalary and minSalary are not passing values
You then have the following WCF service:
[OperationContract]
[WebGet(UriTemplate = "basic/?apiKey={apiKey}&noOfResults={noOfResults}&maxSalary={maxSalary}&minSalary={minSalary}&ouId={ouId}&keywords={keywords}", BodyStyle = WebMessageBodyStyle.Bare)]
public List<SearchResultsDto> BasicSearch(string keywords, string apiKey, int noOfResults, int maxSalary, int minSalary, int ouId)
{
//Do some service stuff
}
This will cause a 400 error, please can someone explain how you pass empty parameters across to a WCF service or is this just not possible?
Currently passing null or an empty parameter is not supported in WCF, the main solution to this problem is to override the querystringconverter which handles the url as it comes through the pipe but before it reaches the operation contract.
An excellent example of implmenting an extension of the querystringconverter is found here:
In the WCF web programming model, how can one write an operation contract with an array of query string parameters (i.e. with the same name)?
HOWEVER
sadly there is a bug in WCF 4 where you cannot override the querystringconverter, this has been addressed by Microsoft and will be fixed in the SP1 release coming this year.
Until then there is no clean way to deal with this situation other than to handle the exception and return a status code of 400 (bad request) - good documentation of the api should handle this in the interim.
Is it just the integers giving you trouble? Maybe you can try making them nullable?
int? MaxSalary
hope this helps
You could send in "-1", and treat that in your business logic as not sent.
It can be handled in multiple ways. Since you are talking about a REST service that can have optional parameters, my suggestion will be do the something like this.
Create a DataObject that will be accepeted as parameter to this method.
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet(RequestFormat=WebMessageFormat.Json)]
RequestObject BasicSearch(RequestObject apiKey);
}
public class Service1 : IService1
{
public RequestObject BasicSearch(RequestObject obj)
{
//Do some service stuff
return obj;
}
}
[DataContract]
public class RequestObject
{
[DataMember]
public string Keywords {get; set;}
[DataMember]
public string ApiKey {get; set;}
[DataMember]
public int NoOfResults { get; set; }
}
Advantages (am going to be short, ping me back for details)
No change in service signature
contract does not change
you will get the flexibility of have
null parameters
you can always extend the number of
parameters without any impact to
existing services
below is the sample input and output from fiddler
note: in the request part i havent passed anything to NumberOfResults intentionally to prove