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

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.

Related

Invalid ModelState error message for Nullable types

I validate the input using ModelState.IsValid:
[HttpGet]
[Route("subjects")]
[ValidateAttribute]
public IHttpActionResult GetSubjects(bool? isActive = null)
{
//get subjects
}
If I pass in the uri ~/subjects/?isActive=abcdef, I get the error message:
The value 'abcdef' is not valid for Nullable`1.
If the input parameter is not nullable
public IHttpActionResult GetSubjects(bool isActive){
//get subjects
}
I get the error message:
The value 'abcdef' is not valid for Boolean.
I want to override the message if nullable type so I can maintain the message ("The value 'abcdef' is not valid for Boolean."). How can I do this since in the ModelState error I don't get the data type. I am implementing the validation as a custom ActionFilterAttribute (ValidationAttribute).
You can change callback that formats type conversion error messages. For example, let's define it right into Global.asax.cs:
public class WebApiApplication : HttpApplication
{
protected void Application_Start()
{
ModelBinderConfig.TypeConversionErrorMessageProvider = this.NullableAwareTypeConversionErrorMessageProvider;
// rest of your initialization code
}
private string NullableAwareTypeConversionErrorMessageProvider(HttpActionContext actionContext, ModelMetadata modelMetadata, object incomingValue)
{
var target = modelMetadata.PropertyName;
if (target == null)
{
var type = Nullable.GetUnderlyingType(modelMetadata.ModelType) ?? modelMetadata.ModelType;
target = type.Name;
}
return string.Format("The value '{0}' is not valid for {1}", incomingValue, target);
}
}
For not nullable types Nullable.GetUnderlyingType will return null, in this case we will use original type.
Unfortunately you cannot access default string resources and if you need to localize error message you must do it on your own.
Another way is to implement your own IModelBinder, but this is not a good idea for your particular problem.
Lorond's answer highlights how flexible asp.net web api is in terms of letting a programmer customize many parts of the API. When I looked at this question, my thought process was to handle it in an action filter rather than overriding something in the configuration.
public class ValidateTypeAttribute : ActionFilterAttribute
{
public ValidateTypeAttribute() { }
public override void OnActionExecuting(HttpActionContext actionContext)
{
string somebool = actionContext.Request.GetQueryNameValuePairs().Where(x => x.Key.ToString() == "somebool").Select(x => x.Value).FirstOrDefault();
bool outBool;
//do something if somebool is empty string
if (!bool.TryParse(somebool, out outBool))
{
HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
response.ReasonPhrase = "The value " + somebool + " is not valid for Boolean.";
actionContext.Response = response;
}
else
{
base.OnActionExecuting(actionContext);
}
}
Then decorate the action method in the controller with the action filter attribute

How to properly handle WCF faults with Silverlight?

No matter how hard I try I cannot seem to be able to handle WCF faults in Silverlight.
In fact the error seems to never leave the server !
E.g. when I debug it, it stops on the line where I throw the FaultException saying it was not handled:
[SilverlightFaultBehavior]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class StoreService : IStoreContract
{
public System.Collections.Generic.List<string> GetStoreDesignNames()
{
try
{
StoreDataContext swdc = new StoreDataContext();
var query = from storeDesign in swdc.StoreDesignDBs select storeDesign.Name;
return query.ToList();
}
catch (System.Data.SqlClient.SqlException sqlExcept)
{
throw new FaultException<SqlFault>(new SqlFault() { Message = sqlExcept.Message });
}
}
}
The class that implements this method derives from a contract interface:
[ServiceContract(Namespace = "Store")]
public interface IStoreContract
{
/// <summary>
/// Obtain the list of store design names.
/// </summary>
[OperationContract,
FaultContract(typeof(SqlFault))]
List<String> GetStoreDesignNames();
}
And the SqlFault class is defined like this:
public class SqlFault
{
public String Message { get; set; }
}
On the client side I handle the error as follow:
// swc is the client
swc.GetStoreDesignNamesCompleted += new EventHandler<ServiceReference.GetStoreDesignNamesCompletedEventArgs>((obj, evt) =>
{
if (evt.Error == null)
{
// In case of success
MessageBox.Show(evt.Result.First());
}
else if (evt.Error is FaultException<ServiceReference.SqlFault>)
{
FaultException<ServiceReference.SqlFault> fault = evt.Error as FaultException<ServiceReference.SqlFault>;
Dispatcher.BeginInvoke(() =>
{
ErrorWindow ew = new ErrorWindow(fault.Detail.Message, "No details");
ew.Show();
});
}
});
swc.GetStoreDesignNamesAsync();
I have tried to put the [SilverlightFaultBehavior] attribute on the interface, to no avail. Even if I do without the interface I still have this error.
I have also tried to use a behavior extension in the web.config as described here but I get a warning saying the extension is not valid.
How does one go about properly handling WCF fault in Siverlight ?
Thanks in advance.
I haven't used WCF (been using WCF RIA Services) but I did come across this article a while ago.
Getting something better than “Server not found.” from WCF in Silverlight
After battling with this for hours I finally hacked something together that works.
This is really a horrible hack and I would have much preferred to use BehaviorExtension for this task. The trick is to set manually the HTTP status code in the body of the WCF method like so:
public System.Collections.Generic.List<string> GetStoreDesignNames()
{
try
{
StoreDataContext swdc = new StoreDataContext();
var query = from storeDesign in swdc.StoreDesignDBs select storeDesign.Name;
return query.ToList();
}
catch (System.Data.SqlClient.SqlException sqlExcept)
{
System.ServiceModel.Web.WebOperationContext ctx = System.ServiceModel.Web.WebOperationContext.Current;
ctx.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
throw new FaultException<SqlFault>(new SqlFault() { Message = sqlExcept.Message });
}
}
The error message then correctly displays on the client side.
If anybody has a better solution than this I'd like to hear it.

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.

FaultException.Detail coming back empty

I am trying to catch a given FaultException on a WCF client. I basically need to extract a inner description from the fault class so that I can then package it in another exception for the upper layers to do whatever.
I've done this successfully a number of time, what makes it different this time is that fault is declared as an array, as you can see from the service reference attribute declared on top of the method that throws the exception:
[System.ServiceModel.FaultContractAttribute(typeof(FaultClass[]), Action = "http://whatever/", Name = "whateverBusinessFault")]
This is my code:
try
{
// call service here
}
catch (FaultException<FaultClass[]> ex)
{
if (ex.Detail != null && ex.Detail.Length > 0)
{
throw new CustomException(ex.Detail[0].description);
}
else
{
throw;
}
}
Problem is Detail (which is an array) is always coming back empty in the code even if I can see the data (description field etc.) in the SOAP response from WCF trace.
So the stuff I need is definitely coming back but for some reason either it doesn't get deserialized or I can't get to it from code.
Any help appreciated!
UPDATE:
Trying with #Darin suggestion but no luck, the string I am extracting from the XmlReader is "/r/n":
var sb = new StringBuilder();
using (XmlReader reader = fault.GetReaderAtDetailContents())
{
while (reader.Read())
sb.AppendLine(reader.ReadOuterXml());
}
var detail = sb.ToString();
Looks like the detail section is not coming up at all!
I found the solution on a UPS Forum :
https://developerkitcommunity.ups.com/index.php/Special:AWCforum/st/id371
"The problem was the visual studio didn't quite map out the ErrorDetail objects right. The ErrorDetail node is called "ErrorDetail", but the type generated for it is "ErrorDetailType." I edited the reference.cs class generated for each service I was using and added a TypeName:"
It is difficult to say where the problem is but I suspect the smoking gun is this axis web service not generating standard message. One way to workaround this would be to parse the XML yourself:
try
{
proxy.CallSomeMethod();
}
catch (FaultException ex)
{
var fault = ex.CreateMessageFault();
using (XmlReader reader = fault.GetReaderAtDetailContents())
{
// TODO: read the XML fault and extract the necessary information.
}
}
It took me ages to figure out how to get the full details message from a FaultException as a string. I eventually figured it out and wrote this extension method:
public static string GetDetail(this FaultException faultException)
{
if (faultException == null)
throw new ArgumentNullException(nameof(faultException));
MessageFault messageFault = faultException.CreateMessageFault();
if (messageFault.HasDetail) {
using (XmlDictionaryReader reader = messageFault.GetReaderAtDetailContents()) {
return reader.ReadContentAsString();
}
}
return null;
}
Originally I was using reader.Value but that only appeared to the return the first line of a multi-line details message. reader.ReadContentAsString() appears to get the whole thing, new lines included, which is what I wanted.
I came up with the simplest test case I could. I hope it will help you.
Server side:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(FaultClass[]))]
string Crash();
}
public class Service1 : IService1
{
public string Crash()
{
var exception = new FaultException<FaultClass[]>(new FaultClass[] { new FaultClass { Data = "TEST" } }, new FaultReason("Boom"));
throw exception;
}
}
[DataContract]
public class FaultClass
{
[DataMember]
public string Data { get; set; }
}
Client side:
try
{
using (var client = new Service1Client())
{
client.Crash();
}
}
catch(FaultException<FaultClass[]> e)
{
//Break here
}
I had a similar situation in trying to communicate data with faults (specifically a stack trace). See this question. I ended up solving it by creating my own serializable stack trace and including it in a derived FaultException class.

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>";
}