Using IOperationBehavior to supply a WCF parameter - wcf

This is my first step into the world of stackoverflow, so apologies if I cock anything up.
I'm trying to create a WCF Operation which has a parameter that is not exposed to the outside world, but is instead automatically passed into the function.
So the world sees this: int Add(int a, int b)
But it is implemented as: int Add(object context, int a, int b)
Then, the context gets supplied by the system at run-time. The example I'm working with is completely artificial, but mimics something that I'm looking into in a real-world scenario.
I'm able to get close, but not quite the whole way there.
First off, I created a simple method and wrote an application to confirm it works. It does. It returns a + b and writes the context as a string to my debug. Yay.
[OperationContract]
int Add(object context, int a, int b);
I then wrote the following code:
public class SupplyContextAttribute : Attribute, IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
if (!operationDescription.Messages.Any(m => m.Body.Parts.First().Name == "context"))
throw new FaultException("Parameter 'context' is missing.");
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new SupplyContextInvoker(dispatchOperation.Invoker);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// Remove the 'context' parameter from the inbound message
operationDescription.Messages[0].Body.Parts.RemoveAt(0);
}
}
public class SupplyContextInvoker : IOperationInvoker
{
readonly IOperationInvoker _invoker;
public SupplyContextInvoker(IOperationInvoker invoker)
{
_invoker = invoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs().Skip(1).ToArray();
}
private object[] IntroduceContext(object[] inputs)
{
return new[] { "MyContext" }.Concat(inputs).ToArray();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
return _invoker.Invoke(instance, IntroduceContext(inputs), out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _invoker.InvokeBegin(instance, IntroduceContext(inputs), callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _invoker.IsSynchronous; }
}
}
And my WCF operation now looks like this:
[OperationContract, SupplyContext]
int Amend(object context, int a, int b);
My updated references no longer show the 'context' parameter, which is exactly what I want.
The trouble is that whenver I run the code, it gets past the AllocateInputs and then throws an Index was outside the bounds of the Array. error somewhere in the WCF guts.
I've tried other things, and I find that I can successfully change the type of the parameter and rename it and have my code work. But the moment I remove the parameter it falls over.
Can anyone give me some idea of how to get this to work (or if it can be done at all).

Well, I figured it out on my own. The MessagePartDescription has an Index property. I just need to resynch these values.
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
var parts = operationDescription.Messages[0].Body.Parts;
parts.RemoveAt(0);
for (int i = 0; i < parts.Count; i++)
parts[i].Index = i;
}

Related

WCF: Injecting custom headers for an operation through decoration

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

How do you implement a base method that should be called for all the methods?

I have a Product Service. On each call to Service, I want to call a method. In this case, I am logging. I am looking for a way, not to write the using statement in each method. But I still want the Logging to happen on each call. How do I do this?
public class ProductService : IProductService
{
public IList<Product> GetProductsByBrand(int BrandID)
{
using (new Logging())
{
// Get a list of products By Brand
}
return new List<Product>();
}
public IList<Product> Search(string ProductName)
{
using (new Logging())
{
// Search
}
return new List<Product>();
}
public static string OrderProducts(IList<Order> Orders, Payment paymentDetials)
{
string AuthCode;
using (new Logging())
{
// Order and get the AuthCode
}
AuthCode = "";
return AuthCode;
}
}
Have you heard of AOP (Aspect Oriented Programming)? It's a way of implementing cross cutting concerns as reusable Aspects that wrap around the target type and perform additional processing before or after the method that they are wrapping.
http://en.wikipedia.org/wiki/Decorator_pattern
Within a WCF environment this is typically done by applying "Behaviors" to your service class. In this case I would suggest the IOperationBehavior interface using an attribute that implements IParameterInspector in order to look at the parameters before they are passed the service instance is created and called. Here is a link to a useful article that goes into more depth regarding your options for extending the wcf message pipeline.
http://msdn.microsoft.com/en-us/magazine/cc163302.aspx
//Attribute class
public class LogOperationBehavior : Attribute, IOperationBehavior, IParameterInspector {
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {
return;
}
public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation) {
//clientOperation.ParameterInspectors.Add(new ClientParameterInspector());
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation) {
dispatchOperation.ParameterInspectors.Add(this);
}
public void Validate(OperationDescription operationDescription) {
return;
}
#region IParameterInspector Members
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState) {
//perform logging after
}
public object BeforeCall(string operationName, object[] inputs) {
//perform logging before
return null;
}
#endregion
}
public class BusinessOperation : IBusinessOperation {
//Apply to your service via an attribute
[LogOperationBehavior]
public DivideResponse DivideTwoNumbers(DivideRequest dr) {
return new DivideResponse() {
Answer = dr.Numerator/ dr.Demoninator2,
};
}
Have you considered creating a logging proxy? It would look something like this:
public class LoggingProductService : IProductService
{
private readonly IProductService _core;
public LoggingProductService(IProductService core)
{
_core = core;
}
public IList<Product> GetProductsByBrand(int BrandID)
{
Log("Getting products for brand " + BrandId);
return _core.GetProductsByBrand(BrandId);
}
//other IProductService methods here, all logging and delegating to _core
private void Log(string message)
{
using (var log = new Logging())
{
log.Write(message);
}
}
}
Of course, I don't entirely understand your Logging interface, so fill in the appropriate guesses with correct code. You also may not want to create and Dispose a Logging that often, I don't know.
You can create a dynamic proxy. See this article for instructions. http://www.drdobbs.com/windows/184405378

After Enable IDispatchMessageFormatter I see nullable parameters in service

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.

WCF - Are Asynchronous Services interoperable?

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! :)
}
}

Callback function is not executed in an asynchronous call

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