Dashboard > Castle Project > Castle Project > Dynamic Proxy 3 design meeting
  Castle Project Log In   View a printable version of the current page.  
  Dynamic Proxy 3 design meeting
Added by Krzysztof Kozmic, last edited by Krzysztof Kozmic on Jul 08, 2009  (view change)
Labels: 
(None)

Dynamic Proxy 3 is going to be the next major version of the project. Here's rough sketch of how we currently envision it, plus discussion on the design.

Dynamic Proxy 2 had few flaws to its internal design. First and most obvious one, was a lot of repetition in the code (ClassProxyGenerator and InterfaceProxyGeneratorWithTarget are to a great degree copy/pasted from one another). It also had four kinds of proxies which introduced some confusion (especially interface proxy with target interface) and sometimes was not enough.

So, while  preserving current API for compatibility I would like to make it more flexible underneath and unify all kinds of proxies to one generation pipeline, parameterizing and externalizing decisions that were hardcoded now. Depending on how it gets structured in the code, we might end up with very flexible type building framework (let's call it Type Builder) and proxy creation framework (let's call it Proxy Factory) built on top of that.

Type Builders

Type Builder would be a library (for a lack of better world. I'm not going to introduce another assembly for that. But depending on how it shapes itself, we might want to put it in the Core, instead of DynamicProxy) that would be able to create model of a type, and then generate a runtime type out if it. The major difference from  the current approach (Emitter classes) is that currently, the classes are forward only. That is, the definition and generation process are tightly coupled which impairs flexibility. That leads to the procedural code we have in ProxyGenerator classes now.

TypeBuilder would be for runtime type what Expression Trees are for compiled methods. They would be an editable model that we could edit, query and enrich, and then compile into a runtime type. If we externalized the 'compile' step into an external entity, we could then use either Reflection.Emit or Mono.Cecil (or something else) to do the work. I'm not convinced this would bring that much value however, and that it's a goal worth pursuing.

Currently we have our four kinds of proxy generators, but if someone wants to affects how the created type looks like they can't do this. For example, let's say a trivial change - adding attributes to the type or some methods currently is not really possible, and if someone wanted to do this, they'd have to basically rewrite the whole proxy generator (or in worst case all four of them).

With this new API it would simply be a matter of single extension point exposed before the 'compile' step where the extension provided would be able to modify the type in any way required (thanks to non-forward only manner the Type Builders would work). This could be also used without the rest of the Dynamic Proxy stuff to generate types that are not proxies (used by Type Wrapping, or external libraries like ReMotion Mixins).

Proxy Factory

 Proxy Factory would be something like current *ProxyGenerator classes but built on top of Type Builder (and *ProxyGenerators would use it under the cover to preserve their current public API).

In a similar fashion to proxy generators we have now, it would build the model of the type to be generated (explicit model using Type Builders, not implicit as it is now) and then compile it into the new type, feed the cache with it etc. From the outside it would do pretty much what it does now. However the how would be substantially changed.

API

From API perspective, I would like to unify the proxy creation to just one method (with overloads)

proxyFactory.CreateProxy (/*some args*/);

With flexibility of Type Builders (plus right extension/decision points) we could now mix and match between kinds of proxies we have now, for example create interface proxy that would allow change target just for selected methods, or class proxy that would wrap an instance of the class much like interface proxy does now.

Generally speaking we have several decisions in the framework that are either hardcoded or outsourced to the extensions points with regard to the type being generated.

  • What should be it's base type
  • Which interfaces should it implement
  • Which methods should it override.
  • Which calls forward to which method of which of given target objects (either the explicit target, the mixed in objects, or to none aka interface proxy without target)
  • Should it be allowed to change the target of invocation

These decisions could be inferred from the parameters passed to the method.

DISCLAIMER: API shown here, is just an example created on the fly. The resulting API may be very much different from what is shown here. It all depends on your feedback.

For example

proxyfactory.CreateProxy(typeof(MyClass),new Type[0],new FooInterceptor());

 Would be the same as:

proxyGenerator.CreateClassProxy(typeof(MyClass),new Type[0],new FooInterceptor());






whereas:

proxyfactory.CreateProxy(typeof(object),new [] { typeof(IFoo), typeof(IBar) },new FooInterceptor());

would do the same thing as current:

proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(IFoo), new[] { typeof(IBar) }, new FooInterceptor());

 This second example also fixes a small inconvenience with the current API. Since all overloads of Create*Proxy* methods take only one parameter as the proxy target type, the additional interfaces to be implemented by proxy is passed as a separate argument. This is not the case for the new proxy so although new syntax is more verbose if we only need one interface for our proxy, it is more natural to use and follows the least surprise principle. This is even more apparent if we consider interface proxy with target.

For the new API it would look like this:

proxyfactory.CreateProxy(typeof(Baz),new [] { typeof(IFoo), typeof(IBar) }, new[] {new Baz()},new FooInterceptor());

With the current API we would write:

proxyGenerator.CreateInterfaceProxyWithTarget(typeof(IFoo), new[] { typeof(IBar) }, new Baz(), new FooInterceptor());

The thing that I know surprises quite a few people with current sybtax is that how the resulting proxy will look depends on whether the type Baz implements both IFoo, and IBar interfaces or just IFoo which on the surface could be perceived as inconsistent with other kinds of proxies. With the new syntax there is no hard suggestion of the goal (for the lack of a better word) of the proxy, so the user does not expect the behavior to be either way. Even more so, with the new extension points it will be completely up to the user, how the proxy will behave.

Also notice, that the target in the sample is passed as an element in an array. This would allow us to pass more than one target to the proxy. Currently we can do this using mixins, which are actually just this - additional targets hardwired to the interfaces they implement with a cool name taken from Ruby world. I think this approach would be much less scary for the newcomers, and the advanced users will appreciate the added flexibility this approach provides. It would also make the issues we have with mixins go away (for example passing mixin instances as part of proxy generation options).

Moreover it would allow for things that are not possible now, like class proxies with target that quite a few users requested.

The flexibility I talked about in choosing the target would be a result of a new decision point exposed to the user. Currently it is by definition hardwired, that proxy methods forward to the same methods on the target object. What I would like to see, is the ability to change that, so that we could do something like this (pseudocode)

public  int override Foo(int bar)
{
      _interceptors.Intercept();
      return _target.Foo2(bar,"some other argument" );

}

This would enable for example duck typing, or type wrapping (more on this below). It would also enable situations where for example more than one mixed in type implement certain interface. We'd now have an extension point that would let us decide where to forward calls to the interface members instead of throwing an exception as it is now.

 Type Wrapping

 Type Wrapping is an idea Glenn Block had regarding data binding in WPF.

Basically (at least from what I understand, although I'm not a WPF guy) databinding in WPF relies on two things to work:

  1. That the given object implements INotifyPropertyChanged interface which raises event when a value of property gets changed.
  2. That the given object has Properties matching names specified in XAML markup

With Type Wrapping we could wrap any type (even sealed) and just create a type that mimics it. Since WPF does not care about the actual type we could use that to decorate any type with databinding capabilities (generating View Model from any Model type, no necessarily unsealed with virtual properties.)

This possibly could also be used to intercept calls in C# 4.0 using the 'dynamic' keyword on any member (including non-virtual) of any type (including sealed).

Caching

Since we're doing some serious changes to the udnerlying model we need to take a good look at how to provide the widest possible caching scope while still not crippling flexibility. We have several things we need to take into account with caching:

  • base type
  • implemented interfaces
  • target types (discussion below)
  • overridden methods
  • call mapping (which methods get forwarded to which target)

 We could leave out target types by storing them as the lowest denominator type between all types they're forwarded to, or as System.Object and then downcast them to the appropriate type when passing to the constructor of invocation.

Also invocations could be shared, between all types (this to some extent may be implemented in v2.2). We'd have basically three kinds of invocation possible for each method:

  • invocation with read-only target - like in interface proxy with target, or class proxy
  • invocation with changeable target - like in interface proxy with target interface, would implement IChangeProxyTarget, but it should be renamed to IChangeInvocationTarget to mitigate confusion that it changes the target of the whole proxy, not only the invocation
  • invocation without target - like in interface proxy without target

Krzysztof, your ideas sounds really good.

I'm not sure how far we need to go with the type builder, but I do agree that there is way too much code in the ProxyGenerator classes that needs to be restructured in a more maintainable and flexible way. When do you think you would need to go in a reverse proxy generation direction, as you mentioned the current process in forward only.

I do like all how everything is called CreateProxy(), very simple from a user's perspective. You don't need to learn all the DP terminology to have a play.

One important feature you pointed out is being able to control the proxy generation at all points, which currently isn't exposed very well. Building the in-memory model would actually make it very easy to write an extension that made changes to the model before the proxy was generated, without having to build extension points all over the place. However, the internal behaviour of DP is much more exposed, which means it needs to be better documented so users could actually write an extension that wasn't based on a guess of how the proxy model is being generated.

I am also a fan of the last idea from Gleen Block for using DP with WPF databinding, when you don't want to, or can't use a DependencyObject.

@Jono

With type builder and added flexibility I want to build a platform for frameworks like DynamicProxy. I'm not sure what you mean by reverse proxy generation direction. What I meant was that I want to decouple how proxy type is built from where it is built, so that at any place before calling Compile you can change it. I'm not yet certain how it would exactly work, and to what extent.

 With the terminology I agree- especially interface proxy with target interface is very confusing to almost everyone I talk to

As to controling generation at all points - that would be one of benefits of the decoupling type builder would provide. As for exposing internal behavior - it would be exposed by the lower layer, but encapsulated by the upper layer. If we do it well, a new user will be able to immediately hop in and use the 20% of the power of the framework without being distracted by the additional complexity of the remaining 80%. On the other hand if he needs to swap something from within - he will have this possibility.

 Thanks for your feedback - I'm glad you like the ideas, now let's get to work

Posted by Krzysztof Kozmic at Jul 01, 2009 07:36; last updated at Jul 01, 2009 07:52

You wrote this in the page:

Krzysztof: The major difference from the current approach (Emitter classes) is that currently, the classes are forward only.

It sounded like you wanted to support going in reverse.

By separating the code that builds the types into a separate platform, we just need to be careful we don't duplicate Reflection.Emit/Cecil/Microsoft "Cecil". It would actually need to be rich rather than just an big abstraction.

I like the direction, it should make it easier for newcomers to use the project as you mentioned. I think the decoupling is the key to 3.0, which will make it possible to reduce the amount of code internally.

However, we should probably focus on getting 2.2 closer to completion before we start making big changes to support 3.0

I'm working on v2.2

So far this is probably all I did towards v3.0 (except talking to some people and thinking about it).

I did no changes in the code base  targeted at v3.0 yet.

 And I meant forward only in the similar sense like XmlWriter is forward only, as compared to XmlDocument which is not.

I'm not an expert of DP, used it mainly via Rhino Mocks. I'm not an expert of Spring.Net's ProxyFactory either, used it via Spring.AOP only. Have no intention to compare the features, just throw in a different approach.

From a user's perspective. I need a proxy builder that when given a class, or one or more target instances, with optional interfaces and AOP advices, I can then by calling ProxyFactory.NewProxy(),  get an instances of object that have all the interfaces implemented for me. If the class is given, I would get an inheritance based proxy, but otherwise an composite based proxy.

I don't know if I expressed myself clearly so here is the Spring.AOP document if that helps

http://www.springframework.net/doc-latest/reference/html/aop.html#aop-proxyfactoryobject

http://www.springframework.net/doc/sdk/1.1/html/Spring.Aop~Spring.Aop.Framework.ProxyFactory_members.html

BTW, it seems to me that we are duplicating the effort in two open source projects that do similar things. It would be ideal to combined the effort. I see DP as a more general purpose solution, maybe Spring.AOP can reuse in future.

Just my 2 cents worth.

Disclaimer: while I do contribute to one of Spring.Net extension project, my opinion has nothing to do with Spring.Net team.

Kenneth,

Take a look at the sketch of the API. What you're suggesting is basically what we have in mind. You more or less can do the same with the current API, provide base class, interfaces, targets (either explicitly, or as mixin instances). We're just planning to unify this API and add more control to accommodate other potential uses, that require dynamic runtime type generation, for example the type wrapping that is discussed above.

If Spring project is willing to take dependency on Castle Dynamic Proxy we're open for the possibility. Our license allows this, so there's no issue there. I don't know anyone from Spring community, but if you raise the issue there, I'll be happy to discuss their needs and potential cooperation.

Krzysztof,

the  "type wrapping" feature would be very useful, particularly if it provides an API to easily "enrich" objects in order to add them some display related properties.

As mentioned in this discussion : http://groups.google.com/group/castle-project-users/browse_thread/thread/41bde4091d51cc61

sometimes  we need to manipulate extra properties in the view layer : here the need is a flag for selection, but it could be  a color or any other meta-information that has sense only in a particular view of the business entity.

As shown in the discussion the problem can be solved by using mixins, but it would benefits from a cooler API like that adds abstraction :

[code]viewEntity = businessEntity.AddDynamicProperties
(
    new DynamicPropertyInfo
    {
        Name="IsSelected",
        Type=typeof(bool)
    },
    new DynamicPropertyInfo
    {
        Name="Color",
        Type=typeof(System.Drawing.Color)
    }
)[/code]

"viewEntity" would appear to be the same as "businessEntity" plus two additional properties.

"AddDynamicProperties" could be implemented as an extension method on "object" :
[code]object AddDynamicProperties(this object entity, params DynamicPropertyInfo[] infos)[/code]

But with the new orientation of C# towards dynamic typing, like in Python or Ruby, maybe this kind of features would become less useful.
Indeed the owner of the business entity could simply do :
[code]businessEntity.IsSelected = ...
businessEntity.Color = ...[/code]
It would be a very powerful solution to the problem discussed in the aforementioned thread.

Be that as it may, thanks for the great job you do, you helped me make my design better.

Posted by serious at Sep 17, 2009 14:24; last updated at Sep 18, 2009 00:51

Wouldn't a simple mixin suffice for that?

Yes, a mixin is the solution finally used but it requires the user to provide the implementation of an interface.

Here even the specification of an interface would be optional because the way WPF locates properties values doesn't care about the objects "static interface" but only of their "dynamic interface".

Not sure, but isn't it a kind of duck typing ?

Are you referring to attached dependency properties in the property system?

I'm not referring to dependency properties  explicitly defined by the user with DependencyProperty.(Register/RegisterAttached) but to "normal" properties that can be used in a DataGrid mapping.

By the way an API dedicated to WPF problematics like creating dynamic properties implementing the INotifyPropertyChanged notification paradigm, would be of great help.

Because  adding properties dynamically is only half the way, the UI having to be notified when the business layer changes.

Site running on a free Atlassian Confluence Community License granted to Castle Project. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.4 Build:#809 Jun 12, 2007) - Bug/feature request - Contact Administrators