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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With