WCF operation in Client expects a different sets of argument than what is defined in server - wcf

I am new to WCF. I have a sample WCF server and a client consuming the service.
I have a OperationContract called getEmployer4 which accepts a EmployerRequestBO and returns a EmployerResponseBO, both these 2 types are decorated as MessageContract
public EmployerResponseBO getEmployer4(EmployerRequestBO rqst)
{
return new EmployerResponseBO
{ CompanyName = "Apple", CompanyAddress = "US" };
}
my EmployerRequestBO looks like:
[MessageContract(IsWrapped = true, WrapperName = "EmployerRequest", WrapperNamespace ="http://mycompany.com/services")]
public class EmployerRequestBO
{
[MessageHeader(Namespace = "http://mycompany.com/services")]
public string LicenseKey
{
get; set;
}
private int _regID;
[MessageBodyMember(Order = 1, Name = "CompanyRegistrationID", Namespace = "http://mycompany.com/services")]
public int RegistrationID
{
get
{
return _regID;
}
set
{
_regID = value;
}
}
Problem is, when i tried to call the operaiton in client with below code:
ServiceReference_EmployerService.EmployerClient client = new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");
ServiceReference_EmployerService.EmployerRequestBO request = new ServiceReference_EmployerService.EmployerRequestBO("ABC123", 123);
ServiceReference_EmployerService.EmployerResponseBO response= client.getEmployer4(request);
The getEmployer4 doesnot expect an EmployerRequestBO argument, Error looks like below
Click to see attachment
There is no argument given that corresponds to the required formal
parameter 'CompanyRegistrationID' of
'EmployerClient.GetEmployer4(string, ref int, out string)'.
Can anyone explain why it is asking for primitive types instead of a MessageContract type? Thanks!

It took quite a bit of time before I learned that, if your Operation communicate through MessageContract, you need to create the proxy like:
ServiceReference_EmployerService.**IEmployer** client =
new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");
whereas if you Operation communicate through DataContract, you need to create the proxy like:
ServiceReference_EmployerService.**EmployerClient** client2 =
new ServiceReference_EmployerService.EmployerClient("BasicHttpBinding_IEmployer");

Related

Calling webservice with complex parameters from c# client

Hello, Here is a class ...
public class Authentification
{
private string userField;
private string passwordField;
public string user
{
get
{
return this.userField;
}
set
{
this.userField = value;
}
}
public string password
{
get
{
return this.passwordField;
}
set
{
this.passwordField = value;
}
}
}
here the web service :
[WebMethod]
public Vehicle[] getVehiculeList(Authentification authentification)
{
....
}
Here the client and the call of webservice :
(the same class Authentification like in the webservice has been defined)
Authentification azz = new Authentification() ;
azz.user = "toto";
azz.password = "tata";
string aa = ws.getVehiculeList(azz);
gives an error :
Error 27 The best overloaded method match for 'WSCL.localhost.Service1.getVehiculeList(WSCL.localhost.Authentification)' has some invalid arguments
and
Error 28 Argument '1': cannot convert from 'WSCL.Authentification' to 'WSCL.localhost.Authentification'
Any help ?
Thank a lot !
What might have happened is that you have referenced the assembly containing the data entities (e.g. Authentication) on your client, and now you have both the proxied entity (WSCL.localhost.Authentification) and the original server entity (WSCL.Authentification). If you change your client's use of Authentication to use the proxied class (WSCL.localhost.Authentification) it should work.
If you switch to WCF, you will be able to move the data entities like Authentication into a separate assembly, and then Share this same type between your Service and your Client. AFAIK this isn't possible 'out of the box' in ASMX.

Data member default values, how to figure out whether something was really sent?

By default, WCF deserializes missing elements into default values like null, 0 or false. The problem with this approach is that if it's a basic type like number 0 I'm not sure whether it means the real value sent by an external system or a default value generated by WCF.
So my question is: Is it possible to find out at run-time whether the default value means "I didn't send anything".
This is crucial because we can't update and overwrite existing data in the database with the default values just because the external system didn't send a particular element this time (data corruption).
Microsoft's short answer is "It is up to the receiving endpoint to appropriately interpret a missing element."
Data member default values
http://msdn.microsoft.com/en-us/library/aa347792.aspx
Can somebody please clarify what's that supposed to mean?
Thanks
If you define your data members as properties, you can use whether the setter was called or not to decide whether some value was sent. The code below shows one data contract which knows whether it deserialized its fields.
public class Post_51ca1ead_2f0a_4912_a451_374daab0101b
{
[DataContract(Name = "Person", Namespace = "")]
public class Person
{
string name;
int age;
bool nameWasSent;
bool ageWasSent;
[DataMember]
public string Name
{
get
{
return this.name;
}
set
{
this.nameWasSent = true;
this.name = value;
}
}
[DataMember]
public int Age
{
get
{
return this.age;
}
set
{
this.ageWasSent = true;
this.age = value;
}
}
[OnDeserializing]
void OnDeserializing(StreamingContext ctx)
{
this.ageWasSent = false;
this.nameWasSent = false;
}
public override string ToString()
{
return string.Format("Person[Name={0},Age={1}]",
nameWasSent ? name : "UNSPECIFIED",
ageWasSent ? age.ToString() : "UNSPECIFIED");
}
}
public static void Test()
{
MemoryStream ms = new MemoryStream();
DataContractSerializer dcs = new DataContractSerializer(typeof(Person));
dcs.WriteObject(ms, new Person { Name = "John", Age = 30 });
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
string noAge = "<Person><Name>John</Name></Person>";
ms = new MemoryStream(Encoding.UTF8.GetBytes(noAge));
object p = dcs.ReadObject(ms);
Console.WriteLine("No age: {0}", p);
string noName = "<Person><Age>45</Age></Person>";
ms = new MemoryStream(Encoding.UTF8.GetBytes(noName));
p = dcs.ReadObject(ms);
Console.WriteLine("No name: {0}", p);
}
}

Message or a type that has MessageContractAttribute and other parameters of different types

I'm developing WCF services where some classes have the [MessageContract] attribute, and some don't.
When I try to run the services I get this error message below:
The operation 'ProcessOperation' 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.
Does it mean that all the services must have [MessageContract] although they are not related?
No, it means that you have multiple parameters on the method and some of them are not messages. Try posting the interface to your service.
This blog post explains:
... problem is that message contracts cannot be used at the same time as other parameter types. In this case, the return value of the operation is a string. Return values are just another output parameter, so this operation is mixing a message contract message with a primitive parameter type. This fails because message contracts give you control of the layout of the SOAP message, preventing the system from melding in these additional parameters.
Important note:
By the way, the error message you get when you try to mix message contracts looks like this.
This basically means that a particular operation is using a combination of message contract types and primitive types in any of the following combinations:
MixType1: Contract type and primitive types as operation parameters
MixType2: Contract type as a parameter and primitive type as return type
MixType3: Primitive type as a parameter and Contract type as return type
Any of the scenarios listed above would generate the error.
Solved!
I can't return String, I have return Greeting object to the client.
using System;
using System.ServiceModel;
using System.Net.Security;
namespace com.blogspot.jeanjmichel.model
{
[MessageContract]
public class Greeting
{
private String userGreeting;
private void SetGreeting()
{
DateTime now = DateTime.Now;
if (now.Hour >= 7 && now.Hour <= 11)
{
this.userGreeting = "Good morning";
}
else if (now.Hour >= 12 && now.Hour <= 17)
{
if (now.Hour == 12 || now.Hour == 13)
{
this.userGreeting = "Good afternoon, it's lunch time!";
}
else
{
this.userGreeting = "Good afternoon";
}
}
else if (now.Hour >= 18 && now.Hour <= 20)
{
this.userGreeting = "Good evening";
}
else
{
this.userGreeting = "Good night";
}
}
[MessageBodyMember(Order = 1, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
public String UserGreeting
{
get { return this.userGreeting; }
}
public Greeting()
{
this.SetGreeting();
}
}
}
using System;
using System.ServiceModel;
using com.blogspot.jeanjmichel.model;
namespace com.blogspot.jeanjmichel.services.contract
{
[ServiceContract(Namespace = "http://jeanjmichel.blogspot.com/services/v0.0.1")]
public interface IGetGreeting
{
[OperationContract]
Greeting GetGreeting(Credential credential);
}
}
using System;
using System.ServiceModel;
using com.blogspot.jeanjmichel.services.contract;
using com.blogspot.jeanjmichel.model;
namespace com.blogspot.jeanjmichel.services
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,
Namespace = "http://jeanjmichel.blogspot.com/services/v0.0.1")]
public class GetGreetingService: IGetGreeting
{
public Greeting GetGreeting(Credential credential)
{
if (String.IsNullOrEmpty(credential.Token))
{
throw new FaultException("Inform the security phrase, and try again.");
}
else
{
if (credential.Token.Equals("mySeCuriTyP#ss"))
{
Greeting g = new Greeting();
return g;
}
else
{
throw new FaultException("Wrong password.");
}
}
}
}
}
When you are using Message object as a parameter, the method should return void
If you have the issue with mixed types of primitive(such as string) and MessageContract as the other type, i.e. one class as return and a string parameter, one way I solved this was switching from MessageContract to DataContract.
The other way to solve this would be to create a class to hold your primitive type as a property, so that both your return and parameter can implement MessageContract.
I ran into this error while maintaining an API in our code. The API suddenly began returning this error for all endpoints.
I had upgraded the initialization method for Ninject to move away from a method that it said was obsolete.
Obsolete method: NinjectWebServiceHostFactory (no error)
New method: NinjectServiceHostFactory (returns error)
The error went away when I reverted the change.

Find Matching OperationContract Based on URI

...or "How to determine which WCF method will be called based on URI?"
In a WCF service, suppose a method is invoked and I have the URI that was used to invoke it. How can I get information about the WCF end point, method, parameters, etc. that the URI maps to?
[OperationContract]
[WebGet(UriTemplate = "/People/{id}")]
public Person GetPersonByID(int id)
{
//...
}
For instance, if the URI is: GET http://localhost/Contacts.svc/People/1, I want to get this information: service name (Service), Method (GetPersonByID), Parameters (PersonID=1). The point is to be able to listen for the request and then extract the details of the request in order to track the API call.
The service is hosted via http. This information is required before the .Net caching can kick in so each call (whether cached or not) can be tracked. This probably means doing this inside HttpApplication.BeginRequest.
FYI I'm hoping to not use reflection. I'd like to make use of the same methods WCF uses to determine this. E.g. MagicEndPointFinder.Resolve(uri)
Here is what I ended up doing, still interested if there is a cleaner way!
REST
private static class OperationContractResolver
{
private static readonly Dictionary<string, MethodInfo> RegularExpressionsByMethod = null;
static OperationContractResolver()
{
OperationContractResolver.RegularExpressionsByMethod = new Dictionary<string, MethodInfo>();
foreach (MethodInfo method in typeof(IREST).GetMethods())
{
WebGetAttribute attribute = (WebGetAttribute)method.GetCustomAttributes(typeof(WebGetAttribute), false).FirstOrDefault();
if (attribute != null)
{
string regex = attribute.UriTemplate;
//Escape question marks. Looks strange but replaces a literal "?" with "\?".
regex = Regex.Replace(regex, #"\?", #"\?");
//Replace all parameters.
regex = Regex.Replace(regex, #"\{[^/$\?]+?}", #"[^/$\?]+?");
//Add it to the dictionary.
OperationContractResolver.RegularExpressionsByMethod.Add(regex, method);
}
}
}
public static string ExtractApiCallInfo(string relativeUri)
{
foreach (string regex in OperationContractResolver.RegularExpressionsByMethod.Keys)
if (Regex.IsMatch(relativeUri, regex, RegexOptions.IgnoreCase))
return OperationContractResolver.RegularExpressionsByMethod[regex].Name;
return null;
}
}
SOAP
private static void TrackSoapApiCallInfo(HttpContext context)
{
string filePath = Path.GetTempFileName();
string title = null;
//Save the request content. (Unfortunately it can't be written to a stream directly.)
context.Request.SaveAs(filePath, false);
//If the title can't be extracted then it's not an API method call, ignore it.
try
{
//Read the name of the first element within the SOAP body.
using (XmlReader reader = XmlReader.Create(filePath))
{
if (!reader.EOF)
{
XmlNamespaceManager nsManager = new XmlNamespaceManager(reader.NameTable);
XDocument document = XDocument.Load(reader);
//Need to add the SOAP Envelope namespace to the name table.
nsManager.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
title = document.XPathSelectElement("s:Envelope/s:Body", nsManager).Elements().First().Name.LocalName;
}
}
//Delete the temporary file.
File.Delete(filePath);
}
catch { }
//Track the page view.
}

How do i get the invoked operation name within a WCF Message Inspector

I'm doing a message inspector in WCF:
public class LogMessageInspector :
IDispatchMessageInspector, IClientMessageInspector
which implements the method:
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
I can get the name of the invoked service with:
instanceContext.GetServiceInstance().GetType().Name
But how do I get the name of the invoked operation?
It's not pretty, but this is what I did to get the operation name:
var action = OperationContext.Current.IncomingMessageHeaders.Action;
var operationName = action.Substring(action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);
var operationName = OperationContext.Current.IncomingMessageProperties["HttpOperationName"] as string;
This approach is similar to others presented here, but uses Path.GetFileName:
Path.GetFileName(OperationContext.Current.IncomingMessageHeaders.Action);
The return value of this method and the format of the path string work quite harmoniously in this scenario:
The characters after the last directory character in path. If the last
character of path is a directory or volume separator character, this
method returns String.Empty. If path is null, this method returns
null.
OperationContext.Current.IncomingMessageHeaders.Action.Split('/').ToList().Last();
Little late to the party but I had to dig a little deeper than existing answers on this question because they seem to involve getting the action name and not the operation name. (Frequently they are the same so getting the action name does, in fact, get the operation name.)
Microsoft's Application Insights SDK Labs' WCF library makes this concerted effort:
private string DiscoverOperationName(OperationContext operationContext)
{
var runtime = operationContext.EndpointDispatcher.DispatchRuntime;
string action = operationContext.IncomingMessageHeaders.Action;
if (!string.IsNullOrEmpty(action))
{
foreach (var op in runtime.Operations)
{
if (op.Action == action)
{
return op.Name;
}
}
}
else
{
// WebHttpDispatchOperationSelector will stick the
// selected operation name into a message property
return this.GetWebHttpOperationName(operationContext);
}
var catchAll = runtime.UnhandledDispatchOperation;
if (catchAll != null)
{
return catchAll.Name;
}
return "*";
}
private string GetWebHttpOperationName(OperationContext operationContext)
{
var name = WebHttpDispatchOperationSelector.HttpOperationNamePropertyName;
if (this.HasIncomingMessageProperty(name))
{
return this.GetIncomingMessageProperty(name) as string;
}
return "<unknown>";
}