I am trying to make a simple asynchronous call with WCF, but the callback function is never executed. Can anyone tell me what is wrong with the code?
I am using visual studio 2008 with .Net 3.5
Service code
[ServiceContract]
public interface IService1
{
[OperationContract(AsyncPattern = true) ]
IAsyncResult BeginGetData(string value, AsyncCallback callback, object state);
string EndGetData(IAsyncResult result);
}
public class Service1 : IService1
{
#region IService1 Members
public IAsyncResult BeginGetData(string value, AsyncCallback callback, object state)
{
return new CompletedAsyncResult<string>(value, state);
}
public string EndGetData(IAsyncResult r)
{
CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
return result.Data;
}
#endregion
}
Client side code
class Program
{
static void Main(string[] args)
{
Service1Client client = new Service1Client();
Console.WriteLine("Start async call");
IAsyncResult result = client.BeginGetData("abc", callback, null);
Console.ReadLine();
}
static void callback(IAsyncResult result)
{
string a = "in callback";
Console.WriteLine(a);
}
}
You need call callback explicitly.
IAsyncResult result = client.BeginGetData("abc", callback, null);
callback(result);
Console.ReadLine();
see reference here.
http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx
Related
Hi I need to inject a custom header in the message if the operation is decorated.
What have I done so far?
1) Created an Attribute by inheriting Attribute and IOperationBehavior
2) Attached a custom OperationInvoker with the operation
Attribute:
public class RankAttribute : Attribute, IOperationBehavior
{
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new PublishMessageInvoker(dispatchOperation.Invoker);
}
//rest of the methods
}
Interface:
public interface INullableService
{
[OperationContract]
[FaultContract(typeof(BusinessServiceException))]
[Rank]
NullableResponse NullChecking(NullableRequest request);
[OperationContract]
[FaultContract(typeof(BusinessServiceException))]
NullableResponse NullChecking2(NullableRequest request);
}
Now the problem is, I dont know where to modify the message header, I get access to the Message via operationDiscription.Messages[] but the documentation says that any modification will yield unexpected results.
Thanks,
Avinash
never mind :) it was trivial :) .. you get access to OperationContext in IOperationInvoker
public class PublishMessageInvoker : IOperationInvoker
{
private IOperationInvoker invoker;
public PublishMessageInvoker(IOperationInvoker invoker)
{
logger.Info("PublishMessageInvoker");
this.invoker = invoker;
}
public object[] AllocateInputs()
{
if (invoker == null)
return null;
return this.invoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader(
"customheader",
"asnjnjdhbhb.com",
"MyAction")
);
return this.invoker.Invoke(instance, inputs, out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
if (invoker == null)
return null;
return this.invoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
if (invoker == null)
{
outputs = null;
return null;
}
return this.invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get {
if (invoker == null) return true;
return this.invoker.IsSynchronous;
}
}
}
I try to implement an Asynchronous Service Operation (http://msdn.microsoft.com/en-us/library/ms731177.aspx) with masstransit request/response.
[ServiceContract]
public interface IService1
{
[OperationContractAttribute(AsyncPattern = true)]
IAsyncResult BeginMyOperation(string data, AsyncCallback callback, object asyncState);
string EndMyOperation(IAsyncResult result);
}
public class Service1 : IService1
{
private string _answer;
public IAsyncResult BeginMyOperation(string data, AsyncCallback callback, object asyncState)
{
return Bus.Instance.BeginPublishRequest(
new MyRequestMessage { Data = data }, callback, asyncState, cfg =>
{
cfg.Handle<MyResponseMessage>(c => _answer = c.Answer);
cfg.SetTimeout(5.Seconds());
});
}
public string EndMyOperation(IAsyncResult result)
{
Bus.Instance.EndPublishRequest<MyResponseMessage>(result);
return _answer;
}
}
But EndPublishRequest throws an Exception "The argument is not an IRequest". Am I doing something wrong?
Yeah, the exception message needs to be a bit better, if you look at the signature though:
public static bool EndPublishRequest<TRequest>(this IServiceBus bus, IAsyncResult asyncResult)
it becomes apparent that the request message type is the required type arg here.
after add my formatter to operation behavior :
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
ServerMessageFormatter Formatter = new ServerMessageFormatter();
dispatchOperation.Formatter = Formatter;
}
In Formatter I have empty Deserialize method cause I want to use default behavior
public void DeserializeRequest(System.ServiceModel.Channels.Message message, object[] parameters)
{}
but In Serialize
public System.ServiceModel.Channels.Message SerializeReply(System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters, object result)
{
//some code
}
Problem is that after enable this class, parameters in service method always was show as null, but in IDispatchMessageInspector class I see that parameters is send properly. I don't know why it's happening, I only add this message formatter code , it is possible that empty class for Deserialize causes this ?
When we are implementing IDispatchMessageFormatter interface usually we are not thinking that method DeserializeRequest is something important as it is not returning any data. This is misleading as method needs to do something.
The easiest way to make parameters correctly passed is to use base DispatchMessageFormatter. Add it to the constructor.
public class ResponseJsonFormatter : IDispatchMessageFormatter
{
IDispatchMessageFormatter basicDispatchMessageFormatter;
OperationDescription Operation;
public ResponseJsonFormatter(OperationDescription operation, IDispatchMessageFormatter inner)
{
this.Operation = operation;
this.basicDispatchMessageFormatter = inner;
}
public void DeserializeRequest(Message message, object[] parameters)
{
basicDispatchMessageFormatter.DeserializeRequest(message, parameters);
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
string json=Newtonsoft.Json.JsonConvert.SerializeObject(result);
byte[] bytes = Encoding.UTF8.GetBytes(json);
Message replyMessage = Message.CreateMessage(messageVersion, Operation.Messages[1].Action, new RawDataWriter(bytes));
replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return replyMessage;
}
}
And initiate it in the behavior:
public class ClientJsonDateFormatterBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// throw new NotImplementedException();
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new ResponseJsonFormatter(operationDescription, dispatchOperation.Formatter);
}
public void Validate(OperationDescription operationDescription)
{
// throw new NotImplementedException();
}
}
You can check the working example here in the github branch DateTimeFormatterWithParams
There is no default behavior if you do not provide your own logic on DeserializeRequest. You need to either reference the existing formatter and delegate manually in your ServerMessageFormater or provide your own logic.
Basically as the question states, if I make my services asynchronous does that mean they aren't interoperable anymore?
As far as the client is concerned, a sync or an async version of the service are identical (see example below). So the sync/async decision does not affect interoperability.
public class StackOverflow_6231864_751090
{
[ServiceContract(Name = "ITest")]
public interface ITest
{
[OperationContract]
string Echo(string text);
[OperationContract]
int Add(int x, int y);
}
[ServiceContract(Name = "ITest")]
public interface ITestAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginEcho(string text, AsyncCallback callback, object state);
string EndEcho(IAsyncResult ar);
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginAdd(int x, int y, AsyncCallback callback, object state);
int EndAdd(IAsyncResult ar);
}
public class Service : ITest
{
public string Echo(string text) { return text; }
public int Add(int x, int y) { return x + y; }
}
public class ServiceAsync : ITestAsync
{
string Echo(string text) { return text; }
int Add(int x, int y) { return x + y; }
public IAsyncResult BeginEcho(string text, AsyncCallback callback, object state)
{
Func<string, string> func = Echo;
return func.BeginInvoke(text, callback, state);
}
public string EndEcho(IAsyncResult ar)
{
Func<string, string> func = (Func<string, string>)((System.Runtime.Remoting.Messaging.AsyncResult)ar).AsyncDelegate;
return func.EndInvoke(ar);
}
public IAsyncResult BeginAdd(int x, int y, AsyncCallback callback, object state)
{
Func<int, int, int> func = Add;
return func.BeginInvoke(x, y, callback, state);
}
public int EndAdd(IAsyncResult ar)
{
Func<int, int, int> func = (Func<int, int, int>)((System.Runtime.Remoting.Messaging.AsyncResult)ar).AsyncDelegate;
return func.EndInvoke(ar);
}
}
public static void Test()
{
foreach (bool useAsync in new bool[] { false, true })
{
Type contractType = useAsync ? typeof(ITestAsync) : typeof(ITest);
Type serviceType = useAsync ? typeof(ServiceAsync) : typeof(Service);
Console.WriteLine("Using {0} service implementation", useAsync ? "Asynchronous" : "Synchronous");
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(serviceType, new Uri(baseAddress));
host.AddServiceEndpoint(contractType, new BasicHttpBinding(), "");
host.Open();
Console.WriteLine("Host opened");
Console.WriteLine("Using the same client for both services...");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
Console.WriteLine(proxy.Add(3, 4));
((IClientChannel)proxy).Close();
factory.Close();
host.Close();
Console.WriteLine("Done");
Console.WriteLine();
}
}
}
If by "making the service asynchronous", you mean using AsyncPattern, then my understanding is that this has no effect on the client at all, only on how the WCF runtime invokes your operations on the server. The client (a WCF client, that is), as ever, makes its own choice over whether to use asynchronous calls.
From this MSDN page:
To define a contract operation X that is executed asynchronously regardless of how it is called in the client application...
and:
In this case, the asynchronous operation is exposed in metadata in the same form as a synchronous operation: It is exposed as a single operation with a request message and a correlated response message. Client programming models then have a choice. They can represent this pattern as a synchronous operation or as an asynchronous one, so long as when the service is invoked a request-response message exchange takes place.
You're services don't have to do anything "special" ... they still behave the same way. You're clients on the other hand have to consume the services using an Asynchronous Pattern. The easiest way to do this is if you use "Add Service Reference...". Simply check a little box that says "Generate Asynchronous Operations" and voila, you can make asynchronous calls to your service...
Here's a good resource: How to: Call WCF Service Operations Asynchronously
Despite the negative comments, the example at the end is well put. Simply subscribe to the <service-name>Completed event and then make an Async call. The call is made, your UI thread is freed up and as soon as the call completes the event is raised! Be careful with your new found powers!
public class Foo
{
private BarServiceClient client;
public Foo()
{
client = new BarServiceClient();
client.DoWorkCompleted += Client_DoWorkCompleted;
}
private void Client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
{
//e.Result contains your result.
}
public void DoBar()
{
client.DoWorkAsync(); //Called asynchronously, UI thread is free! :)
}
}
I have a service that implement the Async pattern:
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginLoadDocument(Byte[] value, AsyncCallback callback, object state);
Boolean EndLoadDocument(IAsyncResult asyncResult);
The "BeginLoadDocument" run a private method "CallBack" in the service side using a ThreadPool:
public IAsyncResult BeginLoadDocument(string id, AsyncCallback callback, object state)
{
PendingAsyncResult<string> asyncResult =
new PendingAsyncResult<string>(id, callback, state);
ThreadPool.QueueUserWorkItem(new WaitCallback(Callback), asyncResult);
return asyncResult;
}
the Callback method load the document and set the result for the "EndLoadDocument".
So far so good, but how I can handle the exceptions?
If I throw an excetion in the server side, I get a FaultedException'1 wasn't handled.
I did try to use the attribute [FaultContract(typeof(InforError))] where "InfoError" is my custum DataMember, but it does not work.
I am building the proxy using the svcutil /a http:....
You can catch an exception client-side as follows:
try {
MyClient.MyCall();
}
catch (FaultException<IOException> exc) {
// Log and handle exception
}
Where the real exception thrown was, in this example, an IOException.
You'll also need a FaultContract, as you indicated you are, on the Service Interface, as such:
[ServiceContract]
public interface IMyService {
[OperationContract]
[FaultContract(typeof(IOException))]
void MyCall();
}
**** EDIT ****
I'm a little fuzzy on something you wrote:
[FaultContract(typeof(InforError))] where "InfoError" is my custum DataMember
What do you mean by 'DataMember'? What's the definition for InfoError?
The [FaultContract] should be defined on the service interface method... in your post you sound like you're trying to add it to the client side; this is not correct. If I modify your example code, it would look like:
[ServiceContract]
public interface IMyService {
[OperationContract(AsyncPattern = true)]
[FaultContract(typeof(InfoErrorException))]
IAsyncResult BeginLoadDocument(Byte[] value, AsyncCallback callback, object state);
string EndLoadDocument(IAsyncResult asyncResult);
If your service interface is decorated as such, the client should be able to receive FaultExceptions when you call EndLoadDocument (provided the exception that was thrown was an InfoErrorException exception).
On the server side, you have to trap exceptions, then wrap them in a FaultException, as such:
catch (IOException exp) {
InfoErrorException myException = new InfoErrorException();
myException.Reason = "I failed: " + exp.Message;
throw new FaultException<InfoErrorException>(myException);
}
I believe (but would have to double-check) that you can also catch a FaultException on the client side without specifying the type... similar to catching the generic System.Exception.
Your try...catch for the FaultException should be in your callback, around the statement to call EndLoadDocument().
Looks like you are using Silverlight.
Problem is that WCF service returns HTTP Status different to 200, so browser do not provide additional data about response to Silverlight Runtime.
The solution is to use custom ErrorHandler to provide necessary HTTP Code:
/// <summary>Sets the HTTP code to 200 for faults.</summary>
public class HttpStatusCode200ErrorHandler : IErrorHandler
{
public Type ServiceType { get; set; }
public HttpStatusCode200ErrorHandler(Type serviceType)
{
ServiceType = serviceType;
}
public bool HandleError(Exception error)
{
return false;
}
public virtual void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
fault.Properties[HttpResponseMessageProperty.Name] =
new HttpResponseMessageProperty { StatusCode = System.Net.HttpStatusCode.OK };
}
}
You can attach it to your service using following ServiceBehavior attribute:
/// <summary>Applies HttpStatusCode200ErrorHandler.</summary>
[AttributeUsage(AttributeTargets.Class)]
public class HttpStatusCode200BehaviorAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(new HttpStatusCode200ErrorHandler(serviceDescription.ServiceType));
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
For more details look at Understanding WCF Faults in Silverlight.