Nancy: Not able to use IOptions pattern with Nancy/TinyIoc

Created on 25 Jan 2016  ·  6Comments  ·  Source: NancyFx/Nancy

I'm trying to implement the Options Pattern (as recommended here: https://docs.asp.net/en/latest/fundamentals/configuration.html#options-config-objects) on a project with NancyFX / TinyIOC but it's not working.

I'm registering the Options on the Startup.cs/ConfigureServices method but when I try to inject the settings on my class TinyIoc throws Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: AppSettings.

I think this is because the Options Pattern uses Microsoft.Extensions.DependencyInjection but Nancy uses TinyIoc as default so TinyIoc tries to resolve IOptions and fails.

Is there a way to use IOptions<> with TinyIoc?

Worst case, can I replace TinyIoc for MS DependencyInjection?

Here's my code:

Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions();
    services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
}

MyService.cs:

public SearchService(IOptions<AppSettings> config)
{
}

Error:

Application startup exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details. ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: Nancy.NancyEngine ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: Nancy.Routing.DefaultRequestDispatcher ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: Nancy.Routing.DefaultRouteResolver ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: Nancy.Routing.RouteCache ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: MyProject.MyService ---> Nancy.TinyIoc.TinyIoCResolutionException: Unable to resolve type: Microsoft.Extensions.OptionsModel.IOptions1[[MyProject.AppSettings, MyProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]``

Some extra info:

"dependencies": {
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.Owin": "1.0.0-rc1-final",
    "Nancy": "1.4.3",
    "Microsoft.Framework.ConfigurationModel": "1.0.0-beta4",
    "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4",
    "Microsoft.Extensions.OptionsModel": "1.0.0-rc1-final"
},

DNX runtime version:

1.0.0-rc1-update1 mono

Thank you very much.

Most helpful comment

@RTodorov It may be appropriate to create a Nancy bootstrapper library that uses Microsoft.Extensions.DependencyInjection as the container in place of TinyIoC, this is done for many different containers already.

All 6 comments

You're not registering IOptions into Nancy's container anywhere so it's not going to work, I'm not sure what that magic "services.Configure(Configuration.GetSection("AppSettings"));" is doing under the hood, but you will need to get that type registered into Nancy's container by either:

  • Creating your own bootstrapper, passing IOptions into its ctor, using that bootstrapper where you're configuring the use of Nancy, then in the bootstrapper register that in ConfigureApplicationContainer
  • If there's more types you want to share, do the same as the above, but instead of passing in the types, pass in a newly configured TinyIocContainer and then return that instance for GetApplicationContainer in your derived bootstrapper.

Hope that makes sense.

I tried registering by doing this:

Nancy.TinyIoc.TinyIoCContainer.Current.Register(typeof(IOptions<>), typeof(OptionsManager<>));

Which BTW is what services.AddOptions(); does under the hood for the Microsoft.Extensions.DependencyInjection

Is this what you mean?

services.Configure() will take the loaded configuration and register on MS DI for when someone requires AppSettings.

I added this on the custom bootstrap:

container.Register<IOptions<ElasticSearchConfig>, OptionsManager<ElasticSearchConfig>>();

and now the error doesn't happen anymore but unfortunately the object is created empty.

When debugging, I realised that this will return the correct populated object:

`var appSettings = app.ApplicationServices.GetService>();``

But this will return an empty object:

var appSettings = Nancy.TinyIoc.TinyIoCContainer.Current.Resolve<IOptions<AppSettings>>();

Still, TinyIoc is taking the place of Ms Framework DI when I request for IOptions. I also realised that all Microsoft.* dlls are on TinyIoc's Ignore list, does it means that it shouldn't "reply" when I ask for a Microsoft.Extensions.OptionsModel.IOptions?

Like I said originally, you need to pass that type instance in, or a configured container, its no good just registering the type itself as it needs to be constructed by the GetService factory.

Hey, now I get what you said, it worked! I sent IApplicationBuilder to the custom bootstrap and from there registered the IOptions<AppSettings> with the value returned from ApplicationServices.GetService().

I'm not sure this is the best solution though, since TinyIoc will act as a gateway between my service and Microsoft.Extensions.DependencyInjection.

@RTodorov It may be appropriate to create a Nancy bootstrapper library that uses Microsoft.Extensions.DependencyInjection as the container in place of TinyIoC, this is done for many different containers already.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jchannon picture jchannon  ·  9Comments

Hell0wor1d picture Hell0wor1d  ·  12Comments

epsitec picture epsitec  ·  5Comments

ndphuong picture ndphuong  ·  3Comments

Radzhab picture Radzhab  ·  11Comments