part 1: The client setup
part 2: The code setup
While at work the other day I was talking to my boss, and the subject of spoofing data came up. He mentioned that for our current project, it would be nice to be able to spoof data on a per-request basis. I thought it would be a fun challenge to tackle, so I spent a few hours last Saturday and came up with a pretty elegant solution.
Before I started, I had a few requirements:
- It had to be completely config driven. The client and target WCF service were to have no idea what was going on.
- It was to be on a per-user/per-request basis. What this means is any dev could turn it on/off and it would only affect him.
- Extensible. If you didn’t like the data I mocked, or needed to be able to simulate slow network traffic, exceptions, large data, etc - you could.
- I only had 5 hours to come up with a solution.
In part 2 I described how I structured our code so I could provide any ITypeFactory we wanted. With that out of the way, I was able to get back to some more of the fun stuff. Armed with IOperationBehavior, IEndpointBehavior, BehaviorExtensionElement, IOperationInvoker, and ConfigurationSection I set out to finish this thing off.
One of my main goals was to make the entire thing config driven. I knew that when I saw the mockdata header I would want to grab what ITypeFactory to use from the config. I figured I would start there.
public class ServiceMockerConfigSection:ConfigurationSection
{
public static ServiceMockerConfigSection ConfigSection = ConfigurationManager.GetSection("ServiceMockerConfigSection") as ServiceMockerConfigSection;
static ServiceMockerConfigSection() { }
[ConfigurationProperty("mockType",IsRequired=true)]
public string MockFactoryType
{
get
{
return this["mockType"].ToString();
}
}
}
and the config:
<configSections>
<section name="ServiceMockerConfigSection" type="VT.Lenio.ServiceMocker.ServiceMockerConfigSection, VT.Lenio.ServiceMocker"/>
</configSections>
<ServiceMockerConfigSection mockType="ServicesMocks.SimpleMockFactory,ServicesMocks"/>
Next up was the custom IOperationInvoker. This is the part that looks for the mockdata header. When found it creates a type specified in the config (in the case above, a SimpleMockFactory), and injects it into the given service.
public class MockServiceOperationInvoker : IOperationInvoker
{
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
try
{
var headers = OperationContext.Current.IncomingMessageHeaders;
if (headers.Any(h => h.Name == "mockdata"))
{
if (instance is ServiceBase)
{
string mockTypeString = ServiceMockerConfigSection.ConfigSection.MockFactoryType;
Type mockType = Type.GetType(mockTypeString);
ITypeFactory mockFactory = Activator.CreateInstance(mockType) as ITypeFactory;
if (mockFactory != null)
((ServiceBase)instance).TypeFactory = mockFactory;
}
}
}
catch
{
//swallow so we don't break anything
}
return Invoker.Invoke(instance, inputs, out outputs);
}
}
The biggest problem I had with this entire process was getting this invoker attached to an operation. To make that happen you have to create an IOperationBehavior. When the behavior executes, you can add your invoker into the pipeline.
Every example I found showed it being done via a custom attribute. Unfortunately, that broke my #1 rule. How could I easily add/remove the behavior if it was coded into the service? Even after searching MSDN I had to come to the conclusion that the only way suggested would be to use that attribute. I wasn’t going to let that stop me!
Having already implemented a IEndpointBehavior, I had a hunch I could use it here as well. If so, it would allow me to add my IOperationBehavior to every operation in a given endpoint, and have it driven from the configuration.
public class MockOperationBehavior : BehaviorExtensionElement, IOperationBehavior, IEndpointBehavior
{
#region IOperationBehavior
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new MockServiceOperationInvoker() { Invoker = dispatchOperation.Invoker };
}
#endregion
#region IEndpointBehavior
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (var op in endpoint.Contract.Operations)
{
op.Behaviors.Add(this);
}
}
#endregion
public override Type BehaviorType
{
get {return typeof(MockOperationBehavior); }
}
protected override object CreateBehavior()
{
return new MockOperationBehavior();
}
}
Basically, when the IEndpointBehavior was executed it would loop through each operation in its contract and add itself as a behavior. (This works because MockOperationBehavior implements both IEndpointBehavior, and IOperationBehavior.) When the IOperationBehavior runs it sets the dispatchInvoker the the MockerOperationInvoker I created above.
The last step was add the configuration to the service host, and it was complete! To tie it together nicely, I added two bookmarklets to my browser. One wrote the cookie, and one removed it. With the click of a button I am able to switch from live data to spoofed data, and neither the client or the service knows it is happening.
What really excites me about this is not that I can spoof data anytime I want, but that WCF is really easy to extend. Go play around with it, I doubt you will regret it!
I tried to make this as clear as I could, but I could be taking some things for granted. If I confused you at all feel free to leave a comment, or contact me. I will try to make it more clear for you.
short link to this post:
asp.net, Fun with .net, wcf
webdev, programming