... Home Contact

Krzysztof Koźmic's blog

Designed in Poland, assembled in Australia.


Show appreciation: My Amazon.com Wish List


Me@Twitter

    Currently reading

    Article Categories

    Archives

    Post Categories

    MyPersonal

    Syndication:

    Castle Dynamic Proxy tutorial part XII: caching

    If you’ve been following the tutorial, you should remember that Castle Dynamic Proxy provides proxying capabilities by generating types at runtime. Dynamic code generation is not a lightweight operation, so pretty important aspect of Dynamic Proxy is its caching mechanism which we’ll going to cover in this post.

    Let’s consider the following piece of a test:

    var proxy1 = generator.CreateClassProxy<Foo>(new FooInterceptor());
    var proxy2 = generator.CreateClassProxy<Foo>(new BarInterceptor(), new FooInterceptor());
    Assert.AreEqual(proxy1.GetType(), proxy2.GetType());

    Will it succeed? The answer is – yes. In this simple case both proxies would be semantically identical, so generator (or more precisely IProxyBuilder that the generator is using) caches the type that has been used during the first call. During the second call it checks the  cache to see if a type meeting it’s criteria already exists, it finds it, so it skips the generation part and moves straight into creating the instance.

    What

    What is taken into consideration when Dynamic Proxy decides if a type can be reused or not?

    • proxy target type obviously. We were creating two proxies for Foo, so the type could be reused. if the other type was proxy for Bar, it would not be able to reuse proxy of Foo, so a new type would be created.
    • additional interfaces to proxy. If we decided that the second proxy should also implement an IBar the first type would not be reused, since it would not implement the interface, so a new type would be created.
    • type of proxy target object. If we were creating interface proxy with target, and one implementation would be of type Foo, and the other of type Bar, a new type would be created. Does this feel counterintuitive? We’re creating interface proxy so why should implementer type be taken into account, right? Well, recall from the previous part, that additional interfaces are implemented differently depending on whether the target object implements them or not, hence it is important and proxy type could not be reused among different ones.
    • proxy generation options. They obviously affect proxy generation so it’s natural that when they differ a new type needs to be created. Here’s which elements of proxy generation options affect caching
      • proxy generation hook. They affect which methods get proxies, and which not, so if for two proxy generation options hooks are different, cached type will not be reused.
      • mixin types. Logical, same types, cached type can be reused.
      • interceptor selector. Well this is not very logical. Since interceptor selector operates on proxy instances, it should be transparent to the cache. However it’s not. This is a result of design decision that interceptor selector is shared among all instances of a proxy type. Different selectors could mean different behavior, hence the type could not be reused.
      • base type for interface proxy. The default is System.Object. However if you changed it, a new type would have to be generated for a new proxy obviously.

    How

    As you can see there are quite a few ingredients that affect caching. There are few things you can make to improve your usage of cached types and decrease number of types that would have to be generated.

    When you provide proxy generation hook, or interceptor selector always override Equals and GetHashCode methods. The default implementation compare referential equality which in most cases is not what you’d want. Two selectors, or hooks may expose exactly the same behavior, but if the type does not override equality methods to tell that fact to the outside world they will be considered different.

    Where

    If instead of the code above we had this:

    var proxy1 = generator1.CreateClassProxy<Foo>(new FooInterceptor());
    var proxy2 = generator2.CreateClassProxy<Foo>(new BarInterceptor(), new FooInterceptor());
    Assert.AreEqual(proxy1.GetType(), proxy2.GetType());

    Notice we now generate each proxy with different generator, would the types be the same? The answer is – it depends.

    As I said, generator uses IProxyBuilder which provides it with proxy types. Builder uses ModuleScope which manages the cache. So if the two generators had the same ModuleScope behind them the answer would be yet – we would get the same type. However if they had different scope each generator would produce its own type.

    This is important to understand, but in real life you’ll probably need just one proxy generator anyway, hence all the proxies would be created in the same scope, which usually is the best option.

    Comments have been closed on this topic.