views:

50

answers:

1

I found this article on Context Variables in an earlier version of Ninject. My question is two-fold. First, how can I get this behavior with Ninject 2? Secondly, do context variables carry through down the request chain? For example, let's say I wanted to replace these calls:

var a = new A(new B(new C())));
var specialA = new A(new B(new SpecialC()));

... with this:

var a = kernel.Get<A>();
var specialA = kernel.Get<A>(With.Parameters.ContextVariable("special", "true"));

Is it possible to set up a binding like this, where the context remembers that it is in a "special" context when it comes time to construct a C?

+1  A: 

Here's some stuff that I use against V2, with ~0 effort to clean it up for you - let me know if you can't disentagle it.

As you surmised, there doesn't seem to be a really explicit API that surfaces the "context parameter, even for nested resolutions" stuff in v2 as-is (it's presence is buried as the 3rd parameter on an overload of the Parameter ctor).

public static class ContextParameter
{
    public static Parameter Create<T>( T value )
    {
        return new Parameter( value.GetType().FullName, value, true );
    }
}

public static class ContextParameterFacts
{
    public class ProductId
    {
        public ProductId( string productId2 )
        {
            Value = productId2;

        }
        public string Value { get; set; }
    }

    public class Repository
    {
        public Repository( ProductId productId )
        {
            ProductId = productId;

        }
        public ProductId ProductId { get; set; }
    }

    public class Outer
    {
        public Outer( Repository repository )
        {
            Repository = repository;
        }
        public Repository Repository { get; set; }
    }

    public class Module : NinjectModule
    {
        public override void Load()
        {
            Bind<ProductId>().ToContextParameter();
        }
    }

    //[ Fact ]
    public static void TwoDeepShouldResolve()
    {
        var k = new StandardKernel( new Module() );
        var o = k.Get<Outer>( ContextParameter.Create( new ProductId( "a" ) ) );
        Debug.Assert( "a" == o.Repository.ProductId.Value );
    }
}

And here's some code [that'll confuse the matter] which demonstrates how I apply it in my context:-

public class ServicesNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<ProductId>().ToContextParameter();

        Bind<Func<ProductId, ResourceAllocator>>().ToConstant( ( productId ) => Kernel.Get<ResourceAllocator>(
            ContextParameter.Create( productId ) ) );
    }
}

public static class NinjectContextParameterExtensions
{
    public static IBindingWhenInNamedWithOrOnSyntax<T> ToContextParameter<T>( this IBindingToSyntax<T> bindingToSyntax )
    {
        return bindingToSyntax.ToMethod( context => (T)context.Parameters.Single( parameter => parameter.Name == typeof( T ).FullName ).GetValue( context ) );
    }
}

As usual, you should go look a the source and the tests - they'll provide you with a far more detailed and relevant answer than I can.

Ruben Bartelink
you might also find this useful: http://stackoverflow.com/questions/1374098/with-parameters-constructorargument-with-ninject-2-0/1375734#1375734
Ruben Bartelink
This is really excellent stuff, and I was able to use it to get pretty close to what I needed. Unfortunately, the "inherited" parameter gets lost any time I bind to a Provider or a method. I could use some tricks to make sure the methods pass the parameters down the line, but it would become a point of complexity and repetition. I think I'll end up just using a different kernel for each context in our system. Thank you for this response.
StriplingWarrior
@StriplingWarrior: Glad it helped. BTW I reckon there are ways and means for you to propagate context in factories like you say (doesnt the context param you to do that? i.e., context.Get<X> rather than _kernel.Get<X> ? The tests show it. Seriously go look at the tests and you'll suss it. Failing that, if you have a short question re how do I hook this to that cleanly and ask it here I or someone like @Ian Davis will be be about.
Ruben Bartelink
I cannot find a `context.Get<X>` method. Can you point me to the tests you're referring to? I've always had to do context.Kernel.Get<X>, so I would have to manually pull the `ShouldInherit` parameters off of the context and pass them through. And when I tried binding to a provider, the objects that were created and given to the provider were not created using the binding parameter I had given. This seems like a bug to me. At any rate, I got it working by creating new kernels with the same modules as the root kernel and then overriding certain bindings.
StriplingWarrior
@StriplingWarrior: I was guessing about the exact way in which one does the context.Get, but I know that there is a generalised mechanism for handling nested activations. Download the complete trunk, do a Find In Files for ShouldInherit and you should be golden. (I havent looked at the source in a month or two so this isnt fresh in my mind - but I know for a fact that you'll have it figured in 5 minutes if you try - the code and the tests are as clean as you'll find.)
Ruben Bartelink