Castle Dynamic Proxy tutorial part X: Interface proxies with target interface

This is part ten of my tutorial on Castle Dynamic Proxy.

The three kinds of proxies we talked about so far (class proxy, interface proxy with and without target) are the only kinds of proxies most users will ever use. There is however one more kind – interface proxies with target interface.

When I say most users will never use it, by most I mean roughly 99%. I, personally haven’t used it. Whole Castle project stack uses it in only one place that I know of – WCF facility. This proxy kind is used in really rare cases, and you can feel free to skip reading this part, because you probably won’t need information provided here anyway.

For those two of you who decided to go along anyway, here’s the explanation of what it is, and how it’s different from the other kinds. As you might infer from the name, it’s quite similar to interface proxy with target. It’s basically different in one important way: it lets you swap target of invocation for a different implementation of the target interface. Let’s look at an example to clear things up a little bit.

Let’s say we have an application that saves some stuff into some storage. We have our primary storage that we want to use most of the time, but when it for some reason is down, we want to use a secondary storage. For example we have a database on a remote server, but when it’s down we want to use an in memory database.

Anyway, in our trivialized example tests might look like this:

public class Tests
{
    private PrimaryStorage _primaryStorage;
    private SecondaryStorage _secondaryStorage;
    private StorageFactory _sut;
 
    public Tests()
    {
        _primaryStorage = new PrimaryStorage {IsUp = true};
        _sut = new StorageFactory(_primaryStorage);
        _secondaryStorage = new SecondaryStorage();
        _sut.SecondaryStorage = _secondaryStorage;
    }
 
    [Fact]
    public void Save_should_use_primaryStorage_when_it_is_up()
    {
        IStorage storage = _sut.GetStorage();
        string message = "message";
        storage.Save(message);
 
        Assert.Empty(_secondaryStorage.Items);
        Assert.NotEmpty(_primaryStorage.Items);
        Assert.Equal(message, _primaryStorage.Items.First());
    }
 
    [Fact]
    public void Save_should_use_secondaryStorage_when_primaryStorage_is_down()
    {
        _primaryStorage.IsUp = false;
        IStorage storage = _sut.GetStorage();
        string message = "message";
        storage.Save(message);
 
        Assert.Empty(_primaryStorage.Items);
        Assert.NotEmpty(_secondaryStorage.Items);
        Assert.Equal(message, _secondaryStorage.Items.First());
    }
 
    [Fact]
    public void Save_should_go_back_to_primaryStorage_when_is_goes_from_down_to_up()
    {
        IStorage storage = _sut.GetStorage();
        string message1 = "message1";
        string message2 = "message2";
        string message3 = "message3";
 
        storage.Save(message1);
        _primaryStorage.IsUp = false;
        storage.Save(message2);
        _primaryStorage.IsUp = true;
        storage.Save(message3);
        IList<object> primary = _primaryStorage.Items;
        IList<object> secondary = _secondaryStorage.Items;
 
        Assert.Equal(2, primary.Count);
        Assert.Equal(1, secondary.Count);
        Assert.Contains(message1, primary);
        Assert.Contains(message3, primary);
        Assert.Contains(message2, secondary);
    }
}

In the tests StorageFactory class is the class that encapsulates the proxying logic, and PrimaryStorage and SecondaryStorage are just sample implementation of the IStorage interface. The interface is as simple as it can get:

public interface IStorage
{
    void Save(object data);
}

The implementation are also very slim for the purpose of the example. SecondaryStorage stores the messages in the list, and exposes it for the tests as a property:

public class SecondaryStorage : IStorage
{
    private IList<object> _items = new List<object>();
 
    public IList<object> Items
    {
        get { return _items; }
    }
 
    public void Save(object data)
    {
        _items.Add(data);
    }
}

PrimaryStorage additionally has IsUp property that we’ll use to determine whether it’s good to use, or not. In the later case we save to the secondary storage, as can be seen in the tests.

public class PrimaryStorage : IStorage
{
    private IList<object> _items = new List<object>();
 
    public IList<object> Items
    {
        get { return _items; }
    }
 
    public bool IsUp { get; set; }
 
    public void Save(object data)
    {
        _items.Add(data);
    }
}

So far we haven’t seen anything interesting. The tests, provided they pass, which they do with our final implementation, clearly show that indeed when IsUp property of primary storage is false, messages get saved to the secondary storage. If we look at StorageFactory, we’ll see that there is nothing we haven’t seen before in this tutorial. Obviously it creates the proxy using method we haven’t used before, but except for that, this is all pretty standard stuff.

public class StorageFactory
{
    private readonly IStorage _primaryStorage;
    private ProxyGenerator _generator;
 
    public StorageFactory(IStorage primaryStorage)
    {
        _primaryStorage = primaryStorage;
        _generator = new ProxyGenerator();
    }
 
    public IStorage SecondaryStorage { private get; set; }
 
    public IStorage GetStorage()
    {
        var interceptor = new StorageInterceptor(SecondaryStorage);
        object storage = _generator.CreateInterfaceProxyWithTargetInterface(typeof(IStorage), _primaryStorage, interceptor);
        return storage as IStorage;
    }
}

The really interesting stuff happens in StorageInterceptor class.

public class StorageInterceptor : IInterceptor
{
    private readonly IStorage _secondaryStorage;
 
    public StorageInterceptor(IStorage secondaryStorage)
    {
        _secondaryStorage = secondaryStorage;
    }
 
    public void Intercept(IInvocation invocation)
    {
        var primaryStorage = invocation.InvocationTarget as PrimaryStorage;
        if (primaryStorage.IsUp == false)
        {
            ChangeToSecondaryStorage(invocation);
        }
        invocation.Proceed();
    }
 
    private void ChangeToSecondaryStorage(IInvocation invocation)
    {
        var changeProxyTarget = invocation as IChangeProxyTarget;
        changeProxyTarget.ChangeInvocationTarget(_secondaryStorage);
    }
}

Here you can see how the target substitution is achieved. For interface proxies with target interface invocations implement additional interface – IChangeProxyTarget. This is true for only this kind of proxies. The interface is very simple – it has only one method:

dptutorial_10_ichangeproxytarget

The signature sais it accepts System.Object instances, but in reality the new target must be an implementer of target interface of the proxy (hence proxy with target interface). The fact that it’s exposed by invocation object and the way it’s integrated into the pipeline has few interesting implications.

First, you can change the target for that invocation, not for the whole proxy. This is important, not so obvious fact. Even if you change the target, it will be only for that one invocation of this one method. Next time the method (or any other method) gets invoked it will be the original target that you’ll receive as the target of invocation, not the one you set last time.

Second, this design makes the switch operation completely transparent to everybody else. Proxy object has no idea anything changed, because it does not affect it in any way. invocation object does not care about it. Any interceptor that is after the current one in the invocation pipeline will have no idea (unless, of course you notify it in some other way) that the object it receives as target of invocation is not the one set originally.

If you do want to leverage this feature, consider putting the interceptor that may change the target in front of the pipeline (unless in your scenario other approach makes more sense). This will help you avoid cases where you waste cycles doing some work on a target that gets discarded, and then other interceptors get target object that is not fully compliant with rules you set. For example if you have three interceptors in the pipeline: first one performs low level validation (not null etc), second one swaps target and the third one does logging, the third one may work with assumption, that target object has been validated and assume it is valid, which may not be the case if target object was swapped by the second interceptor, in which case the first interceptor will not get the chance to validate it.

Technorati Tags: ,

Comments

Diego says:

Hi,

10x for the article..good stuff.
I think I might be in the 1% group..
I’ll do some testing tomorrow & get back to you on this..

Diego

Sud says:

Hi,
This was a great article. I have the following problem:
1. A web service registers with a parent service with a WCF CallbackContract which is of same type as the original ServiceContract (the parent also implements the same ServiceContract)
2. The parent stores the callback reference somewhere in a dictionary
3. When an external client invokes a parent operation on the ServiceContract, the parent should dynamically delegate the call to the registered child through the Callback (based on some business logic)

Now, I want to move step 3 to an "interceptor" mechanism and DP seems like a right choice (since I would have multiple such services going through the registration and delegation process)

Effectively, I want to setup an interceptor on the WCF server side to intercept the calls and then delegate to children via the CallbackContract.

I looked at WCF extensions, but it doesn’t seem the right choice since I want to dynamically change the target for the same interface.

Any help is greatly appreciated.

Thanks.

@Sud

so what is it exactly that you’re having problems with?