It seems like the problem I'm having is something to do with LinFu/
DynamicProxy2 not being able to call a constructor with arguments -
even if there's no interception on the object in question.
Here's a simple test case to show what I'm trying to do (and fails
with both LinFu and DynamicProxy2 but not with DummyProxyFactory):
public class SomeObject
{
}
public class SomeOtherObject
{
public readonly SomeObject someObj;
public SomeOtherObject(SomeObject someObj)
{
this.someObj = someObj;
}
}
public class SomeInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
}
}
[Test]
public void Whatever()
{
var module = new InlineModule(
m => m.Bind<SomeObject>().ToSelf(),
m => m.Bind<SomeOtherObject>().ToSelf(),
// Doesn't matter what condition I put here
m =>
m
.Intercept<SomeInterceptor>(When.Request.Method.Name.StartsWith("asfd"))
);
using (var kernel = new StandardKernel(
// LinFuModule also fails,
new DynamicProxy2Module(),
module))
{
var obj = kernel.Get<SomeOtherObject>();
Assert.That(obj, Is.Not.Null);
Assert.That(obj.someObj, Is.Not.Null);
}
}
Any ideas as to what I'm doing wrong?
Cheers,
Michael
Secondly, LinFu finds the constructor ok, but what threw me was that
the inclusion of an interceptor that won't even activate on any object
suddenly broke all of my existing intercepted objects because their
private fields were suddenly null.
So I guess if we want to include even just one teensy weensy little
interceptor in a single object, we have to change our entire injected
codebase to use public virtual properties? Even for objects that
previously didn't expose any public fields?
That's rather unfortunate!
Let me know if my thinking is right on this.
Cheers,
Michael
On 20/06/2008, at 12:03 AM, Nate Kohari wrote:
> Michael:
>
> What's actually happening here is that Ninject activates the type
> first, and then wraps it in a proxy. The result of this is that
> you're actually testing the wrong field. What you should do instead
> is create a virtual property which exposes the someObj dependency,
> and then test against that.
>
> I'll readily admit it's confusing the way this works, but it's
> largely due to the way the proxy factories have to create types --
> if they can't override the implementation of a given member, they
> will just create a new one that hides it. In order to make something
> interceptable, it has to either be part of an interface, or it needs
> to be marked as virtual so it can be overridden.
>
> I might go back and see if I can make this a little more user-
> friendly. :)
>
> (By the way, thanks for filing the bugs! Keep them coming if you
> find any others.)
>
>
> Thanks,
> Nate
>
> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael...@gmail.com
You don't have to change all of your private fields to be public
virtual properties - just any (public? protected?) methods that access
those fields. Is that correct?
Still a bit disconcerting to have to change all non-intercepted
classes too - but I guess there's no real way for Ninject to know
ahead of time which objects will be intercepted or not, so all
injected objects are proxied - is that right?
Michael
>> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael.hart.au@gmail.com
Michael
On 20/06/2008, at 11:48 AM, Nate Kohari wrote:
> Michael:
>
> Yes, and this is really a limitation of the way dynamic proxying
> support works on the .NET framework. Without direct language or
> runtime support, methods can only be proxied if they are virtual, or
> if you interact with them via an interface. This unfortunate
> limitation is shared by all runtime AOP systems for .NET that don't
> get into more exotic things like IL weaving and the like.
>
> The main reason why you're seeing this behavior is that you're using
> dynamic interception via the fluent interface in your module.
> Because dynamic interception examines the context of the method call
> itself, you're right -- Ninject has no way of knowing which types
> would be intercepted, so it has to wrap everything in a proxy.
>
> However, bear in mind this is only true with *dynamic* interception.
> If you instead use static interception via the InterceptAttribute,
> only the type that is decorated will be proxied.
>
> I believe that the problem that you're seeing is that your test is
> actually reading the field defined in the *proxy*, whereas the
> constructor is setting the field that is defined in the *actual*
> instance. Changing it to a private field wrapped in a public virtual
> property will provide a means to get at the field defined inside the
> actual instance from outside the proxy, and should solve your problem.
>
>
> -Nate
>
> On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael...@gmail.com
> >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael...@gmail.com
> On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael.hart.au@gmail.com
> >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael.hart.au@gmail.com
I haven't delved into any of the concepts deeply enough to figure out
what issues there are surrounding this - I'm basically going off this:
http://www.codeproject.com/KB/cs/LinFuPart6.aspx
And thinking that the LinFu classes/interfaces mentioned there could
be supported somehow by the wraparound interception framework already
in Ninject. Who knows - maybe they already can? I'm not in a position
to check at the moment.
Does this make sense?
Michael
On 20/06/2008, at 9:27 PM, Nate Kohari wrote:
> Michael:
>
> The main issue with IL weaving is that it has to be done at compile
> time. The main reason AOP is typically integrated with dependency
> injection frameworks is that the framework already has an activation
> pipeline that the instance is being passed through as it is created
> and injected. From there, it's a simple step to wrap it in a proxy.
>
> IL weaving, in contrast, doesn't really fit with a dependency
> injection framework, since it's just an additional step in the build
> process. After you compile your code, you run the IL weaver, and it
> alters the bytecodes that were outputted from the compiler. After
> that, the interception logic is literally part of your code itself,
> and you don't need any outside interaction to make it work.
>
> If you're interested in another take on AOP, IL weaving is pretty
> cool stuff, and it can work in conjunction with a dependency
> injection framework without the framework actually supporting it
> directly.
>
>
> -Nate
>
> On Fri, Jun 20, 2008 at 12:40 AM, Michael Hart <michael...@gmail.com
> > On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael...@gmail.com
> > >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael...@gmail.com
> On Fri, Jun 20, 2008 at 12:40 AM, Michael Hart <michael.hart.au@gmail.com
> > On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael.hart.au@gmail.com
> > >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael.hart.au@gmail.com
So the weaving is just done to prepare the classes for, well, whatever
you want - just like turning them into proxies. Then the behaviour is
all specified at runtime.
Do a search in the linked article for "DynamicProxy Compatibility" and
see if it sounds like the two could co-exist with Ninject - it'd be
great if they could!
Cheers,
Michael
On 20/06/2008, at 10:14 PM, Nate Kohari wrote:
> Michael:
>
> The main problem is that you can't weave IL at runtime, and Ninject
> is geared totally around wiring things at runtime (which is what you
> need to set up dependency injection). If there was a way to weave IL
> at runtime, I'd be all over it, but the .NET JITter doesn't provide
> the necessary hooks unless you're willing to hook into the profiler
> API, which is all unmanaged code and is generally not designed for
> production apps.
>
> That being said, I haven't looked for awhile, so maybe I'll give
> LinFu and Cecil another glance and see if they've come up with some
> sort of wizardry. :)
>
>
> -Nate
>
> On Fri, Jun 20, 2008 at 7:47 AM, Michael Hart <michael...@gmail.com
> > On Fri, Jun 20, 2008 at 12:40 AM, Michael Hart <michael...@gmail.com
> > > On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael...@gmail.com
> > > >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael...@gmail.com
> On Fri, Jun 20, 2008 at 7:47 AM, Michael Hart <michael.hart.au@gmail.com
> > On Fri, Jun 20, 2008 at 12:40 AM, Michael Hart <michael.hart.au@gmail.com
> > > On Thu, Jun 19, 2008 at 8:24 PM, Michael Hart <michael.hart.au@gmail.com
> > > >> On Thu, Jun 19, 2008 at 12:52 AM, Michael Hart <michael.hart.au@gmail.com
I've created a LinFuAopModule that connects IProxyFactory to a
LinFuAopProxyFactory to activate any objects that implement
IModifiableType - that is, any objects that have been "postwoven" by
LinFu.AOP.
This means that there is no need to use DynamicProxies to enable
interception, and all my classes can stay as is, oh joy. The only
issues I've faced so far have been problems with LinFu.AOP Postweaver
working on certain large projects - however, this is unrelated to the
Ninject module.
So here's the code - I've copied it inline because I wasn't sure
whether the attachment would get through. It's all in one file and not
particularly well documented - it'd be good to get some feedback as to
whether my approach is usable or not, and if so, I'm happy to clean it
up a bit.
----8<----
using LinFu.AOP.Interfaces;
using Ninject.Core;
using Ninject.Core.Activation;
using Ninject.Core.Infrastructure;
using Ninject.Core.Interception;
namespace Ninject.Contrib.LinFuAop
{
public class LinFuAopModule : StandardModule
{
public override void BeforeLoad()
{
// We will use a method replacement strategy that checks
// first whether the method should be replaced
// The other option is to use
SimpleMethodReplacementProviderFactory
// which will always try to replace the method
Kernel.Components.Connect<IMethodReplacementProviderFactory>(
new CheckingMethodReplacementProviderFactory());
Kernel.Components.Connect<IProxyFactory>(new
LinFuAopProxyFactory());
}
public override void Load()
{
}
}
public class LinFuAopProxyFactory : ProxyFactoryBase
{
public override object Wrap(IContext context, object instance)
{
IModifiableType modified = instance as IModifiableType;
if (modified != null)
{
modified.IsInterceptionEnabled = true;
modified.MethodReplacementProvider =
Kernel.Components.Get<IMethodReplacementProviderFactory>()
.Create(Kernel, context, instance);
}
// Could add an else check in here and wrap in a
DynamicProxy...
return instance;
}
public override object Unwrap(IContext context, object proxy)
{
IModifiableType modified = proxy as IModifiableType;
if (modified != null)
{
modified.IsInterceptionEnabled = false;
modified.MethodReplacementProvider = null;
}
return proxy;
}
}
public interface IMethodReplacementProviderFactory :
IKernelComponent
{
IMethodReplacementProvider Create(IKernel kernel, IContext
context, object instance);
}
// Creates a MethodReplacementProvider that checks whether it
should
// replace the method or not based on the Ninject context/request
public class CheckingMethodReplacementProviderFactory :
KernelComponentBase, IMethodReplacementProviderFactory
{
public IMethodReplacementProvider Create(IKernel kernel,
IContext context, object instance)
{
return new CheckingMethodReplacementProvider(kernel,
context, instance);
}
}
// Uses LinFu's inbuilt SimpleMethodReplacementProvider and will
always
// try to replace the method
public class SimpleMethodReplacementProviderFactory :
KernelComponentBase, IMethodReplacementProviderFactory
{
public IMethodReplacementProvider Create(IKernel kernel,
IContext context, object instance)
{
return new SimpleMethodReplacementProvider(
new LinFuAopWrapper(Kernel, context, instance));
}
}
// Very similar to the existing LinFuWrapper, just modified for
LinFu.AOP
public class LinFuAopWrapper : StandardWrapper, IMethodReplacement
{
public LinFuAopWrapper(IKernel kernel, IContext context,
object instance)
: base(kernel, context, instance)
{
}
public object Invoke(IInvocationContext context)
{
IRequest request =
Kernel.Components.Get<IRequestFactory>().Create(Context, Instance,
context.TargetMethod, context.Arguments,
context.TypeArguments);
IInvocation invocation = CreateInvocation(request);
invocation.Proceed();
return invocation.ReturnValue;
}
}
// Check whether we have any interceptors for the given context
before
// replacing the method
public class CheckingMethodReplacementProvider :
BaseMethodReplacementProvider
{
public IKernel Kernel { get; set; }
public IContext Context { get; set; }
public object Instance { get; set; }
public CheckingMethodReplacementProvider(IKernel kernel,
IContext context, object instance)
{
Kernel = kernel;
Context = context;
Instance = instance;
}
protected override bool ShouldReplace(IInvocationContext
context)
{
IRequest request =
Kernel.Components.Get<IRequestFactory>().Create(Context, Instance,
context.TargetMethod, context.Arguments,
context.TypeArguments);
IInterceptorRegistry interceptorRegistry =
Kernel.Components.Get<IInterceptorRegistry>();
// Would be nicer to have an
IInterceptorRegistry.HasInterceptors(IRequest)
return interceptorRegistry.GetInterceptors(request).Count
> 0;
}
protected override IMethodReplacement
GetReplacement(IInvocationContext context)
{
return new LinFuAopWrapper(Kernel, Context, Instance);
}
}
}
----8<----
Cheers,
Michael