Using Ninject instead of Funq

369 views
Skip to first unread message

JulianR

unread,
Jun 15, 2011, 10:14:42 AM6/15/11
to servic...@googlegroups.com
Hi,

Throughout our project we already use Ninject and this is currently causing an awkward (but workable) situation with ServiceStack.

What I currently do, in the Global.asax.cs:

    public override void Configure(Container container)
    {
        GlobalKernel.Configure();

        container.Register<IKernel>(GlobalKernel.Instance);
    }

And all my services have this constructor:

    public GetUserService(IKernel kernel) : base(kernel) { }

Where the base class used here then uses that kernel for resolving dependencies that have been registered on the Ninject kernel. I would rather not have to call the base constructor like that nor have to register my dependencies on the Funq container. Another nice thing would be if I could control the lifetime of my services; since they don't contain any state I might as well let them only be instantiated once.

Is there a way this is currently possible? I've seen the recent addition of the Adapter property on the Funq container, but I'm unsure how this is supposed to be used. I've tried the following and it fails right after it resolves IKernel with an unknown error:

    container.Adapter = new NinjectIocAdapter(GlobalKernel.Instance);

    private class NinjectIocAdapter : IContainerAdapter
    {
      private readonly IKernel _kernel;

      public NinjectIocAdapter(IKernel kernel)
      {
        _kernel = kernel;
      }

      public T Resolve<T>()
      {
        return _kernel.Get<T>();
      }

      public T TryResolve<T>()
      {
        return _kernel.Get<T>();
      }
    }

Thanks! : )

Demis Bellot

unread,
Jun 15, 2011, 10:38:07 AM6/15/11
to servic...@googlegroups.com
Hi Julian,

The ServiceStack auto-wiring does both constructor and property injection, I have a tendency to prefer property injection since it ends up with less code.

With the IContainerAdapter, ServiceStack uses Resolve<T> to auto wire the constructor dependencies and TryResolve<T> to auto wire the public property dependencies.
The source code above looks like it should work? what is the exact unknown exception you're seeing? you should be able to debug (i.e. the .pdbs are shipped with the downloads) and find out what's going on there.

As for the lifetime, each service lasts as long as the request, straight after the request if your service is IDisposable it will call Dispose() so you can use this in your base class to add some extra generic logic in your base class to clean up any resources if you need to. The instances aren't re-used, this is a decision I've taken early on this since its easier to understand the lifetime and behaviour of each service. I only want to support one way or the other and new instances offer more flexibility and predictability and makes sense for services getting injected with the IRequestContext.

Hope this explains things a bit better.

Cheers,

JulianR

unread,
Jun 15, 2011, 3:04:06 PM6/15/11
to servic...@googlegroups.com
Thanks for the quick answer!

I downloaded the source so I could debug the error and this is as far as I can trace it back, in ServiceEntry.Generic.cs:

    // Track for disposal if necessary
    if (Owner == Owner.Container && instance is IDisposable)
      Container.TrackDisposable(instance);

It fails with a NullReferenceException. The call to TrackDisposable is what fails. I can't really tell if Container here is null or if it throws inside.

Tim Scott

unread,
Jun 20, 2011, 4:14:13 PM6/20/11
to ServiceStack .NET Open Source REST Web Services Framework
I'm getting the same exception -- using StructureMap. IAppHost his
being resolved from my container via the IContainerAdapter I created.
The next thing is the exception.

Here is my stack trace:

at Funq.ServiceEntry`2.InitializeInstance(TService instance) in C:\src
\ServiceStack\src\ServiceStack\Funq\ServiceEntry.Generic.cs:line 36
at Funq.Container.ResolveImpl[TService](String name, Boolean
throwIfMissing) in C:\src\ServiceStack\src\ServiceStack\Funq
\Container.cs:line 115
at Funq.Container.TryResolveNamed[TService](String name) in C:\src
\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 389
at Funq.Container.TryResolve[TService]() in C:\src\ServiceStack\src
\ServiceStack\Funq\Container.Overloads.cs:line 336
at lambda_method(Closure , Container )
at Funq.Container.ResolveImpl[TService](String name, Boolean
throwIfMissing) in C:\src\ServiceStack\src\ServiceStack\Funq
\Container.cs:line 114
at Funq.Container.ResolveNamed[TService](String name) in C:\src
\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 283
at Funq.Container.Resolve[TService]() in C:\src\ServiceStack\src
\ServiceStack\Funq\Container.Overloads.cs:line 230
at lambda_method(Closure )
at
ServiceStack.ServiceHost.ExpressionTypeFunqContainer.CreateInstance(Type
type) in C:\src\ServiceStack\src\ServiceStack\ServiceHost
\ExpressionTypeFunqContainer.cs:line 137
at
ServiceStack.ServiceHost.ServiceController.&amp;lt;&amp;gt;c__DisplayClass4.&amp;lt;Register&amp;gt;b__3(IRequestContext
requestContext, Object dto) in C:\src\ServiceStack\src\ServiceStack
\ServiceHost\ServiceController.cs:line 209
at ServiceStack.ServiceHost.ServiceController.Execute(Object
request, IRequestContext requestContext) in C:\src\ServiceStack\src
\ServiceStack\ServiceHost\ServiceController.cs:line 311
at
ServiceStack.WebHost.Endpoints.EndpointHost.ExecuteService(Object
request, EndpointAttributes endpointAttributes, IHttpRequest httpReq)
in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints
\EndpointHost.cs:line 179
at
ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.ExecuteService(Object
request, EndpointAttributes endpointAttributes, IHttpRequest httpReq)
in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints\Support
\EndpointHandlerBase.cs:line 162
at
ServiceStack.WebHost.Endpoints.RestHandler.GetResponse(IHttpRequest
httpReq, Object request) in C:\src\ServiceStack\src\ServiceStack
\WebHost.EndPoints\RestHandler.cs:line 90
at
ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest
httpReq, IHttpResponse httpRes, String operationName) in C:\src
\ServiceStack\src\ServiceStack\WebHost.EndPoints\RestHandler.cs:line
61

Tim Scott

unread,
Jun 20, 2011, 7:07:21 PM6/20/11
to ServiceStack .NET Open Source REST Web Services Framework
I built from source and stepped through. Container is null. There
are a lot of external invocations, so I'm having trouble seeing what's
actually causing the problem.

In any case, I'd say it's a bug. Let me know if I can help
troubleshoot. In the meantime I'll fall back to "poor man's DI" :(

Demis Bellot

unread,
Jun 20, 2011, 7:58:09 PM6/20/11
to servic...@googlegroups.com
Hi Tim,

Thanks for the StackTrace that helps shows where the problem is.
I just need to try to reproduce the bug on my side before I can fix it.

In the meantime if you can send a small test case/example that reproduces this bug that would help a lot.

Thanks,

Demis Bellot

unread,
Jun 20, 2011, 11:22:38 PM6/20/11
to servic...@googlegroups.com
Hi Guys,

I've tracked and fixed the bug (with thanks to Julians help), it was to do with resolving an IDisposable dependency from an external IContainerAdapter

The updated dlls are now v2.25 here:

I've also uploaded the latest dlls to NuGet v2.95 which should be available to download/update shortly.

Thanks,

Tim Scott

unread,
Jun 21, 2011, 11:14:14 AM6/21/11
to servic...@googlegroups.com
Awesome, thanks for the quick response. That exception is gone.  

Now I get exceptions resolving IRequestContext and IHttpRequest from StructureMap.  I tried adding the following to my container setup:

factory.For<IRequestContext>().Use<HttpRequestContext>();
factory.For<IHttpRequest>().Use<HttpListenerRequestWrapper>();

And now I get this exception.  This makes sense because HttpListenerRequestWrapper has concrete c-tor parameters.  Seems like ServiceStack uses the container for a lot of it's own stuff.  How to make that work with my container?

StructureMap Exception Code:  205
Missing requested Instance property "operationName" for InstanceKey "e2d8df05-d3a7-47d5-b756-5d22c6b2f3e0"

   at StructureMap.Pipeline.ConstructorInstance.&amp;lt;.ctor&amp;gt;b__0(String key)
   at StructureMap.Util.Cache`2.get_Item(KEY key)
   at StructureMap.Pipeline.ConstructorInstance.Get(String propertyName, Type pluginType, BuildSession session)
   at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
   at StructureMap.Pipeline.Arguments.Get[T](String propertyName)
   at lambda_method(Closure , IArguments )
   at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.&amp;lt;&amp;gt;c__DisplayClass2.&amp;lt;CreateBuilder&amp;gt;b__0(IArguments args)
   at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
   at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
   at StructureMap.Pipeline.ConstructorInstance.build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.ObjectBuilder.ConstructNew(Type pluginType, Instance instance, BuildSession session)
   at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
   at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
   at StructureMap.BuildSession.&amp;lt;&amp;gt;c__DisplayClass3.&amp;lt;.ctor&amp;gt;b__1()
   at StructureMap.BuildSession.CreateInstance(Type pluginType)
   at StructureMap.Pipeline.DefaultInstance.build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.ConstructorInstance.Get(String propertyName, Type pluginType, BuildSession session)
   at StructureMap.Pipeline.ConstructorInstance.Get[T](String propertyName, BuildSession session)
   at StructureMap.Pipeline.Arguments.Get[T](String propertyName)
   at lambda_method(Closure , IArguments )
   at StructureMap.Construction.BuilderCompiler.FuncCompiler`1.&amp;lt;&amp;gt;c__DisplayClass2.&amp;lt;CreateBuilder&amp;gt;b__0(IArguments args)
   at StructureMap.Construction.InstanceBuilder.BuildInstance(IArguments args)
   at StructureMap.Pipeline.ConstructorInstance.Build(Type pluginType, BuildSession session, IInstanceBuilder builder)
   at StructureMap.Pipeline.ConstructorInstance.build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.SmartInstance`1.build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.createRawObject(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.Instance.Build(Type pluginType, BuildSession session)
   at StructureMap.Pipeline.ObjectBuilder.ConstructNew(Type pluginType, Instance instance, BuildSession session)
   at StructureMap.Pipeline.ObjectBuilder.Resolve(Type pluginType, Instance instance, BuildSession session)
   at StructureMap.BuildSession.CreateInstance(Type pluginType, Instance instance)
   at StructureMap.BuildSession.&amp;lt;&amp;gt;c__DisplayClass3.&amp;lt;.ctor&amp;gt;b__1()
   at StructureMap.BuildSession.CreateInstance(Type pluginType)
   at StructureMap.Container.GetInstance(Type pluginType)
   at StructureMap.Container.GetInstance[T]()
   at StructureMap.ObjectFactory.GetInstance[PLUGINTYPE]()
   at MyRecordz.Ocr.WebApi.ServiceLocatorIocAdapter.TryResolve[T]() in C:\LunaverseRepositories\MyRecordz\src\MyRecordz.Ocr.WebApi\ServiceLocatorIocAdapter.cs:line 18
   at Funq.Container.&amp;lt;GetEntry&amp;gt;b__8[TService,TFunc](Container c) in C:\src\ServiceStack\src\ServiceStack\Funq\Container.cs:line 274
   at Funq.Container.ResolveImpl[TService](String name, Boolean throwIfMissing) in C:\src\ServiceStack\src\ServiceStack\Funq\Container.cs:line 112
   at Funq.Container.TryResolveNamed[TService](String name) in C:\src\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 389
   at Funq.Container.TryResolve[TService]() in C:\src\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 336
   at lambda_method(Closure , Container )
   at Funq.Container.ResolveImpl[TService](String name, Boolean throwIfMissing) in C:\src\ServiceStack\src\ServiceStack\Funq\Container.cs:line 112
   at Funq.Container.ResolveNamed[TService](String name) in C:\src\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 283
   at Funq.Container.Resolve[TService]() in C:\src\ServiceStack\src\ServiceStack\Funq\Container.Overloads.cs:line 230
   at lambda_method(Closure )
   at ServiceStack.ServiceHost.ExpressionTypeFunqContainer.CreateInstance(Type type) in C:\src\ServiceStack\src\ServiceStack\ServiceHost\ExpressionTypeFunqContainer.cs:line 137
   at ServiceStack.ServiceHost.ServiceController.&amp;lt;&amp;gt;c__DisplayClass4.&amp;lt;Register&amp;gt;b__3(IRequestContext requestContext, Object dto) in C:\src\ServiceStack\src\ServiceStack\ServiceHost\ServiceController.cs:line 209
   at ServiceStack.ServiceHost.ServiceController.Execute(Object request, IRequestContext requestContext) in C:\src\ServiceStack\src\ServiceStack\ServiceHost\ServiceController.cs:line 311
   at ServiceStack.WebHost.Endpoints.EndpointHost.ExecuteService(Object request, EndpointAttributes endpointAttributes, IHttpRequest httpReq) in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints\EndpointHost.cs:line 179
   at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.ExecuteService(Object request, EndpointAttributes endpointAttributes, IHttpRequest httpReq) in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints\Support\EndpointHandlerBase.cs:line 162
   at ServiceStack.WebHost.Endpoints.RestHandler.GetResponse(IHttpRequest httpReq, Object request) in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints\RestHandler.cs:line 90
   at ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, String operationName) in C:\src\ServiceStack\src\ServiceStack\WebHost.EndPoints\RestHandler.cs:line 62

Demis Bellot

unread,
Jun 21, 2011, 1:30:24 PM6/21/11
to servic...@googlegroups.com
The IRequestContext and IHttpRequest (which is available via base.RequestContext.Get<IHttpRequest>) is not made available in the container as they are created only once on a per-request basis.

I'm not sure what the expected behaviour is here:
factory.For<IRequestContext>().Use<HttpRequestContext>();
factory.For<IHttpRequest>().Use<HttpListenerRequestWrapper>();

But you can't access this statically, if you don't get the right instance that is generated once per request, it's just a useless empty request.
Right now they only way to get it is by having your service implement IRequiresRequestContext which has the behaviour of injecting the RequestContext (and IHttpRequest see above) into your service.

You may want to override OnBeforeExecute() in your base class so you can access the IRequestContext in your IService and then do what you want with it generically.

Let me know if this does/doesn't work for you as I can try look into other avenues of making the IRequestContext more available.

Cheers,

Tim Scott

unread,
Jun 21, 2011, 2:53:52 PM6/21/11
to servic...@googlegroups.com
I might have expressed myself badly.  My code has does not want to instantiate or access IRequestContext and IHttpRequest -- from my container or elsewhere.  Rather ServiceStack asking me for these.  That is, during each request ServiceStack calls TryResolve on my adapter for both of these types.  But they are not in my container.  I don't know what it expects me to return.

Demis Bellot

unread,
Jun 21, 2011, 2:57:24 PM6/21/11
to servic...@googlegroups.com
Oh sorry, just return null ;)

I should probably hide this from delegating to the ContainerAdapter, will do in next version.
Reply all
Reply to author
Forward
0 new messages