3

Suppose we have an interface ICat that is derived from ICatBase and ICatExtension as shown below. For both distinct interfaces, an implementation is available, CatBase and CatExtension. How can Castle's DynamicProxy be used to merge these into an instance of ICat?

Is it possible to create a proxy in which ICatExtension is implemented by CatExtension and ICatBase is 'implemented' by an interceptor? How can this be achieved?

public interface ICatBase
{
   string Name { get; set; }
   int Age { get; set; }
}

public interface ICatExtension
{
   string Description { get; }
}

public interface ICat : ICatBase, ICatExtension
{
}

public class CatBase : ICatBase
{
   public string Name { get; set; }
   public int Age { get; set; }
}

public class CatExtension : ICatExtension
{
   public string Description
   {
      get { return "Furry"; }
   }
}

EDIT

I have been trying to use mixins to make this work, but the code below results in a NotImplementedException.

var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions();
options.AddMixinInstance(new CatBase());
options.AddMixinInstance(new CatExtension());
var cat  = generator.CreateInterfaceProxyWithoutTarget<ICat>(options);        
cat.Name = "Joey";

This is a DynamicProxy2 error: There are no interceptors specified for method 'Void set_Name(System.String)' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)

I could create a custom interceptor that intercepts calls and dispatches to the correct interface, but I feel there must be an easier/better way. Am I correct?

EDIT #2

Thank you, Krzysztof! Using the lines below was the solution.

var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions();
options.AddMixinInstance(new CatBase());
options.AddMixinInstance(new CatExtension());

var cat = (ICat)generator.CreateClassProxyWithTarget(typeof(object), new[] { typeof(ICat)}, new object(), options);

EDIT #3

As a final step, I have configured a Windsor container to create the proxy from this example. The only way I was able to do this, was by specifying a name "Cat" and resolving an instance of System.Object by specifying the name and casting to the ICat interface afterwards.

WindsorContainer container = new WindsorContainer();
container.Register(
    Castle.MicroKernel.Registration.Component.For<object>().
        Named("Cat").
        Proxy.AdditionalInterfaces(typeof (ICat)).
        Proxy.MixIns(new CatBase()).
        Proxy.MixIns(new CatExtension())
    );

var cat = (ICat)container.Resolve(typeof(object), "Cat");

Is there a more elegant way to this in which I can just ask the container for an ICat instance, without referring to a particular name?

Ringo
  • 83
  • 6
  • I don't remember if it's documented anywhere yet, but you see the correct behavior. `ICat` with no target takes precedence over your mixins. See how it works: http://issues.castleproject.org/issue/DYNPROXY-96 – Krzysztof Kozmic Nov 19 '10 at 11:39
  • Okay, so the complete interface `ICat` should have been implemented by the target or one of the mixins. But doesn't that prohibit me from actually merging two interfaces? I would like to get an instance of `ICat` (combined from `ICatBase` and `ICatExtension`) where part of the implementation (`ICatBase`) is dispatched to one target/mixin and the other (`ICatExtension`) is dispatched to another instance. More specifically, we will provide an implementation for `ICatExtension` and the `ICatBase` should be automatically generated with an interceptor. – Ringo Nov 19 '10 at 13:12
  • no it only means that the target takes precedence over mixins. Just don't make this interface your target. Create class proxy for `System.Object` and toss this interface as an additional interface to proxy, this way it will have the lowest priority and the calls will go to mixins – Krzysztof Kozmic Nov 19 '10 at 13:33
  • Great! I have added the solution to the post. – Ringo Nov 20 '10 at 09:27

1 Answers1

1

Mixins are what you'd normally use for that.

Krzysztof Kozmic
  • 27,267
  • 12
  • 73
  • 115