How to allow a plugin webAPI to override an existing webAPI - c#

I have a Web API controller, say EmployeeController, which we register using Autofac. Now we create another controller with the same name and route, but with different functionality. When we try to register this new EmployeeController (i.e., Plugin) using Autofac, we would get an exception like
multiple types were found that match the controller named EmployeeController.
My objective is to successfully inject the second controller and override the functionality of the first controller with it.
Project A - > Core Project
namespace Main.API
{
public class EmployeeController : ApiController
{
// Some Logic
}
}
Project B - > Plug-in Project
Later consumer want to override employee controller with same controller name
namespace Plugin.API
{
public class EmployeeController : ApiController
{
// Some Logic
}
}
Autofac
// assemblies contains Main.API.dll & Plugin.API.dll
builder.RegisterApiControllers(assemblies.ToArray()).InstancePerRequest();

In order to implement what you want I would use AOP concept which will make it easier to implement and more powerful.
Castle DynamicProxy project provides AOP concept for .net and can be used by Autofac with the Autofac.Extras.DynamicProxy2 nuget package.
You will have only 1 EmployeeController in your main project
namespace Main.API
{
public class EmployeeController : ApiController
{
public virtual String Get(Int32 id)
{
// Some Logic
}
}
}
and various IInterceptor in your plugin projects :
namespace Plugin
{
public class XEmployeeeControllerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if(!invocation.Method.Name == nameof(Core.APi.EmployeeController.Get))
{
return;
}
invocation.Proceed();
// alter return value
invocation.ReturnValue = invocation.ReturnValue + "-intercepted";
}
}
}
Then register things like this:
builder.RegisterApiControllers(assemblies.ToArray())
.InstancePerRequest()
.EnableClassInterceptors();
builder.RegisterAssemblyTypes(assemblies.ToArray())
.As<IInterceptor>();
See Type Interceptors for more information

Using this following code snippet you can override the same name of plugin controller.
public class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
private readonly HttpConfiguration _configuration;
private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
/// <summary>
/// custom http controllerselector
/// </summary>
/// <param name="config"></param>
public CustomHttpControllerSelector(HttpConfiguration config) : base(config)
{
_configuration = config;
_controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
}
/// <summary>
/// GetControllerMapping
/// </summary>
/// <returns></returns>
public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
return _controllers.Value;
}
private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
{
var controllers = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
foreach (Type t in controllerTypes)
{
var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
//Remove Core API Controller and add the Plugin API controller.
if (controllers.Keys.Contains(controllerName) && t.Namespace.Contains("Plugin"))
{
controllers.Remove(controllerName);
}
if (!controllers.Keys.Contains(controllerName))
{
controllers[controllerName] = new HttpControllerDescriptor(_configuration, t.Nam`enter code here`e, t);
}
}
return controllers;
}
}

Related

MVC 6 Custom Model Binder with Dependency Injection

Right now my ViewModel looks like this:
public class MyViewModel
{
private readonly IMyService myService;
public ClaimantSearchViewModel(IMyService myService)
{
this.myService = myService;
}
}
My Controller that consumes this ViewModel looks like this:
public class MyController : Controller
{
private readonly IMyService myService;
public HomeController(IMyService myService)
{
this.myService = myService;
}
public IActionResult Index()
{
var model = new MyViewModel(myService);
return View(model);
}
[HttpPost]
public async Task<IActionResult> Find()
{
var model = new MyViewModel(myService);
await TryUpdateModelAsync(model);
return View("Index", model);
}
}
What I need is my Controller to look like is this:
public class MyController : Controller
{
private readonly IServiceProvider servicePovider;
public MyController(IServiceProvider servicePovider)
{
this.servicePovider = servicePovider;
}
public IActionResult Index()
{
var model = servicePovider.GetService(typeof(MyViewModel));
return View(model);
}
[HttpPost]
public IActionResult Index(MyViewModel model)
{
return View(model);
}
}
Right now, calling the first Index method works fine (with
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource(x => x.Name.Contains("ViewModel")));
in my Startup class) but doing the POST to Index(MyViewModel model) gives you a No parameterless constructor defined for this object exception. I realize that a custom model binder that can use my DI will be the most likely solution... but I'm not able to find any help on how to even get started here. Please help me with this, especially for Autofac in MVC 6.
We got the answer here: https://github.com/aspnet/Mvc/issues/4167
And the answer is to use: [FromServices]
My Model ends up looking like this:
public class MyViewModel
{
[FromServices]
public IMyService myService { get; set; }
public ClaimantSearchViewModel(IMyService myService)
{
this.myService = myService;
}
}
Although it's sad to make that property public, it's much less sad than having to use a custom model binder.
Also, supposedly you should be able to pass [FromServices] as part of the param in the Action method, it does resolve the class, but that breaks the model binding... ie none of my properties got mapped. It looks like this: (but again, THIS DOES NOT WORK so use the above example)
public class MyController : Controller
{
... same as in OP
[HttpPost]
public IActionResult Index([FromServices]MyViewModel model)
{
return View(model);
}
}
UPDATE 1
After working with the [FromServices] attribute we decided that property injection in all of our ViewModels was not the way we wanted to go, especially when thinking about long term maintenance with testing. SO we decided to remove the [FromServices] attributes and got our custom model binder working:
public class IoCModelBinder : IModelBinder
{
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{
var serviceProvider = bindingContext.OperationBindingContext.HttpContext.RequestServices;
var model = serviceProvider.GetService(bindingContext.ModelType);
bindingContext.Model = model;
var binder = new GenericModelBinder();
return binder.BindModelAsync(bindingContext);
}
}
It's registered like this in the Startup ConfigureServices method:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinders.Clear();
options.ModelBinders.Add(new IoCModelBinder());
});
And that's it. (Not even sure that options.ModelBinders.Clear(); is needed.)
UPDATE 2
After going through various iterations of getting this to work (with help https://github.com/aspnet/Mvc/issues/4196), here is the final result:
public class IoCModelBinder : IModelBinder
{
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
{ // For reference: https://github.com/aspnet/Mvc/issues/4196
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
if (bindingContext.Model == null && // This binder only constructs viewmodels, avoid infinite recursion.
(
(bindingContext.ModelType.Namespace.StartsWith("OUR.SOLUTION.Web.ViewModels") && bindingContext.ModelType.IsClass)
||
(bindingContext.ModelType.IsInterface)
)
)
{
var serviceProvider = bindingContext.OperationBindingContext.HttpContext.RequestServices;
var model = serviceProvider.GetRequiredService(bindingContext.ModelType);
// Call model binding recursively to set properties
bindingContext.Model = model;
var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);
bindingContext.ValidationState[model] = new ValidationStateEntry() { SuppressValidation = true };
return result;
}
return await ModelBindingResult.NoResultAsync;
}
}
You'd obviously want to replace OUR.SOLUTION... with whatever the namespace is for your ViewModels Our registration:
services.AddMvc().AddMvcOptions(options =>
{
options.ModelBinders.Insert(0, new IoCModelBinder());
});
UPDATE 3:
This is the latest iteration of the Model Binder and its Provider that works with ASP.NET Core 2.X:
public class IocModelBinder : ComplexTypeModelBinder
{
public IocModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, ILoggerFactory loggerFactory) : base(propertyBinders, loggerFactory)
{
}
protected override object CreateModel(ModelBindingContext bindingContext)
{
object model = bindingContext.HttpContext.RequestServices.GetService(bindingContext.ModelType) ?? base.CreateModel(bindingContext);
if (bindingContext.HttpContext.Request.Method == "GET")
bindingContext.ValidationState[model] = new ValidationStateEntry { SuppressValidation = true };
return model;
}
}
public class IocModelBinderProvider : IModelBinderProvider
{
private readonly ILoggerFactory loggerFactory;
public IocModelBinderProvider(ILoggerFactory loggerFactory)
{
this.loggerFactory = loggerFactory;
}
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!context.Metadata.IsComplexType || context.Metadata.IsCollectionType) return null;
var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
foreach (ModelMetadata property in context.Metadata.Properties)
{
propertyBinders.Add(property, context.CreateBinder(property));
}
return new IocModelBinder(propertyBinders, loggerFactory);
}
}
Then in Startup:
services.AddMvc(options =>
{
// add IoC model binder.
IModelBinderProvider complexBinder = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
int complexBinderIndex = options.ModelBinderProviders.IndexOf(complexBinder);
options.ModelBinderProviders.RemoveAt(complexBinderIndex);
options.ModelBinderProviders.Insert(complexBinderIndex, new IocModelBinderProvider(loggerFactory));
This question is tagged with ASP.NET Core, so here's our solution for dotnet core 3.1.
Outline of our solution: TheProject needs to make ICustomerService available to an object created automatically in the request pipeline. Classes that need this are tagged with an interface, IUsesCustomerService. This interface is then checked by the Binder on object creation, and special case is handled.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
namespace TheProject.Infrastructure.DependencyInjection
{
/// <summary>
/// This is a simple pass through class to the binder class.
/// It gathers some information from the context and passes it along.
/// </summary>
public class TheProjectModelBinderProvider : IModelBinderProvider
{
public TheProjectModelBinderProvider()
{
}
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
ILoggerFactory ilogger;
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// The Binder that gets returned is a <ComplexTypeModelBinder>, but I'm
// not sure what side effects returning early here might cause.
if (!context.Metadata.IsComplexType || context.Metadata.IsCollectionType)
{
return null;
}
var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
foreach (ModelMetadata property in context.Metadata.Properties)
{
propertyBinders.Add(property, context.CreateBinder(property));
}
ilogger = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory));
return new TheProjectModelBinder(propertyBinders, ilogger);
}
}
/// <summary>
/// Custom model binder.
/// Allows interception of endpoint method to adjust object construction
/// (allows automatically setting properties on an object that ASP.NET creates for the endpoint).
/// Here this is used to make sure the <see cref="ICustomerService"/> is set correctly.
/// </summary>
public class TheProjectModelBinder : ComplexTypeModelBinder
{
public TheProjectModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, ILoggerFactory loggerFactory)
: base(propertyBinders, loggerFactory)
{
}
/// <summary>
/// Method to construct an object. This normally calls the default constructor.
/// This method does not set property values, setting those are handled elsewhere in the pipeline,
/// with the exception of any special properties handled here.
/// </summary>
/// <param name="bindingContext">Context.</param>
/// <returns>Newly created object.</returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var customerService = (ICustomerService)bindingContext.HttpContext.RequestServices.GetService(typeof(ICustomerService));
bool setcustomerService = false;
object model;
if (typeof(IUsesCustomerService).IsAssignableFrom(bindingContext.ModelType))
{
setcustomerService = true;
}
// I think you can also just call Activator.CreateInstance here.
// The end result is an object that's constructed, but no properties are set yet.
model = base.CreateModel(bindingContext);
if (setcustomerService)
{
((IUsesCustomerService)model).SetcustomerService(customerService);
}
return model;
}
}
}
Then in the startup code, make sure to set AddMvcOptions.
public void ConfigureServices(IServiceCollection services)
{
// ...
// asp.net core 3.1 MVC setup
services.AddControllersWithViews()
.AddApplicationPart(assembly)
.AddRazorRuntimeCompilation()
.AddMvcOptions(options =>
{
IModelBinderProvider complexBinder = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
int complexBinderIndex = options.ModelBinderProviders.IndexOf(complexBinder);
options.ModelBinderProviders.RemoveAt(complexBinderIndex);
options.ModelBinderProviders.Insert(complexBinderIndex, new Infrastructure.DependencyInjection.TheProjectModelBinderProvider());
});
}

instantiates class using ninject ioc in .net

I am using Ninject to do some IoC in my ASP.NET MVC application.
I have an interface "IService.cs" :
public interface IService
{
string method();
}
I have the corresponding implementation "Service.cs" :
public class Service
{
string method()
{
return "result";
}
}
I have done the binding in another class heriting from NinjectModule :
public class MyNinjectModule : NinjectModule
{
public override void Load()
{
RegisterServices();
}
private void RegisterServices()
{
Kernel.Bind<IService>().To<Service>();
}
}
I have my class A which use this service :
public class A
{
private readonly IService _service;
private int i;
public A(IService service, int i)
{
this._service=service;
this.i=i;
}
}
The problem is that now, I don't know how to instantiate my class A in my application. This is where am I stuck, how can I call Ninject
to tell my app to go get the implementation of my interface:
var myClass=new A(????)
The main problem is that your Service class does not implement IService.
public class Service
{
string method()
{
return "result";
}
}
It should be
public class Service : IService
{
public string method()
{
return "result";
}
}
But as for instantiating a class, the best approach is to use a composition root to build an object graph. In MVC, that is best handled by implementing IControllerFactory.
public class NinjectControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public NinjectControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null
? null
: (IController)this.kernel.Get(controllerType);
}
}
Usage
using System;
using Ninject;
using DI;
using DI.Ninject;
using DI.Ninject.Modules;
internal class CompositionRoot
{
public static void Compose()
{
// Create the DI container
var container = new StandardKernel();
// Setup configuration of DI
container.Load(new MyNinjectModule());
// Register our ControllerFactory with MVC
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(container));
}
}
In Application_Start, add:
CompositionRoot.Compose();
You will also need to create an interface for your class A and register it. An integer cannot be resolved automatically, you have to do that explicitly.
Kernel.Bind<IClassA>().To<A>()
.WithConstructorArgument("i", 12345);
And then you would add your dependency to a controller. Dependencies of dependencies are resolved automatically.
public class HomeController : Controller
{
private readonly IClassA classA;
public HomeController(IClassA classA)
{
if (classA == null)
throw new ArgumentNullException("classA");
this.classA = classA;
}
public ActionResult Index()
{
// Use this.classA here...
// IService will be automatically injected to it.
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
}

How do I specify which registration of an interface to use with a particular mvc controller?

I have two implementations of an interface that in plain old c# would be instantiated like so:
var useCache = bool.Parse(ConfigurationManager.AppSettings["useCache"]);
var oven = useCache
? new CachedCookieOven(new CookieOven())
: new CookieOven();
var controller = new CookieController(oven); // MVC Controller
here is the interface and classes:
public interface ICookieOven {
IEnumerable<Cookie> Bake();
}
public class CookieOven : ICookieOven {
public IEnumerable<Cookie> Bake() {
var list = new List<Cookie>();
// bake cookies and return them
return list;
}
}
public class CachedCookieOven : ICookieOven {
readonly ICookieOven _oven;
public CachedCookieOven(ICookieOven oven) { _oven = oven; }
public IEnumerable<Cookie> Bake() {
var cookies = GetFromPlate();
return cookies ?? _oven.Bake();
}
}
My MVC controller has the following constructor
public class CookieController : Controller {
readonly ICookieOven _oven;
public CookieController(ICookieOven oven) { _oven = oven; }
public ActionResult ViewCookies() {
var bakedCookies = _oven.Bake();
return View(bakedCookies);
}
}
The Bootstrapper class that is created says in the comments that I don't need to register my mvc controller classes
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
}
}
In Unity I have registered both instances. There may be a better way and if so tell me.
public static class Bootstrapper {
// ...
private static IUnityContainer BuildUnityContainer() {
var container = new UnityContainer();
var useCache = bool.Parse(ConfigurationManager.AppSettings["useCache"]);
// register
container.RegisterType<ICookieOven, CookieOven>("oven");
if (useCache) {
container.RegisterType<ICookieOven, CachedCookieOven>("cachedOven",
new InjectionConstructor(container.Resolve<ICookieOven>("oven"));
}
}
}
How do I ensure that the correct instance of ICookieOven gets sent to the constructor of the CookieController mvc controller?
Registering types in Unity without a name makes that the default type. If you want to register more than one type, you have to provide a name. The following is the correct way to register my types in the Bootstrapper class:
public static void RegisterTypes(IUnityContainer container)
{
var useCache = bool.Parse(ConfigurationManager.AppSettings["useCache"]);
if (useCache) {
// named, this is not the default
container.RegisterType<ICookieOven,CookieOven>("oven");
// this one is not named and is the default
container.RegisterType<ICookieOven,CachedCookieOven>(new InjectionConstructor(
container.Resolve<ICookieOven>("oven"));
} else {
// notice it is not named, it is the default
container.RegisterType<ICookieOven,CookieOven>();
}
}
You want to create an object but which one depends on a value only known at runtime. What you need is a factory (couple of examples here).
To implement this, one approach could be like this: your controller could depend on a IOvenFactory, injected in controller's constructor. When you need the oven you can call _ovenFactory.Create().
In an IOvenFactory implementation, you could have the logic of how to create, depending on the configuration value.

Ninject and ASP.NET Web API

Before I set up the question you should know that I got my current code from this page:
http://www.strathweb.com/2012/05/using-ninject-with-the-latest-asp-net-web-api-source/
I'm trying to use ASP.NET Web API and Ninject in my application by using an IDependencyResolver adapter found on the site above.
I created all the code just like it shows on the site and it works but when I load up my appication my regular controllers fail and show this error:
[MissingMethodException: No parameterless constructor defined for this object.]
[InvalidOperationException: An error occurred when trying to create a controller of type 'AccountManager.Controllers.HomeController'...
So, it seems like I can use Ninject with regular controllers or Web API controllers but not both. :(
Here is my code:
NinjectResolver.cs
public class NinjectResolver : NinjectScope, IDependencyResolver
{
private IKernel _kernel;
public NinjectResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectScope(_kernel.BeginBlock());
}
}
NinjectScope.cs
public class NinjectScope : IDependencyScope
{
protected IResolutionRoot resolutionRoot;
public NinjectScope(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).SingleOrDefault();
}
public IEnumerable<object> GetServices(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).ToList();
}
public void Dispose()
{
IDisposable disposable = (IDisposable)resolutionRoot;
if (disposable != null) disposable.Dispose();
resolutionRoot = null;
}
}
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
private void SetupDependencyInjection()
{
//create Ninject DI Kernel
IKernel kernel = new StandardKernel();
//register services with Ninject DI container
RegisterServices(kernel);
//tell asp.net mvc to use our Ninject DI Container
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
}
}
AccountingController.cs
public class AccountingController : ApiController
{
private ICustomerService _customerService;
public AccountingController(ICustomerService service)
{
_customerService = service;
}
// GET /api/<controller>/5
public string Get(int id)
{
return "value";
}
}
Insert the following line of code into the CreateKernel() method before the call to the RegisterServices(kernel); is made.
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
You will also need to use the below code, I prefer to have it defined in the same class.
public class NinjectResolver : NinjectScope, IDependencyResolver
{
private IKernel _kernel;
public NinjectResolver(IKernel kernel) : base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectScope(_kernel.BeginBlock());
}
}
public class NinjectScope : IDependencyScope
{
protected IResolutionRoot resolutionRoot;
public NinjectScope(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).SingleOrDefault();
}
public IEnumerable<object> GetServices(Type serviceType)
{
IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return resolutionRoot.Resolve(request).ToList();
}
public void Dispose()
{
IDisposable disposable = (IDisposable)resolutionRoot;
if (disposable != null) disposable.Dispose();
resolutionRoot = null;
}
}
Run it, and it should work. This worked for me, I hope it does for you too.
Further Reading :
Using Ninject – Dependency Injection with ASP.NET Web API controllers
I have a Web API project working using exactly the same solution as you from strathweb, so I just added a normal controller to the project, and it does work. Not a great help on it's own for you, so I'll detail the setup I've got:
I have the following packages installed (on the IOC side of things):
Ninject 3.0.1.10
Ninject MVC 3.0.0.6
Ninject.Web.Common 3.0.0.7
WebActivator 1.5.1
I have absolutely nothing in my Global.asax.cs file regarding Ninject, instead using the NinjectWebCommon.cs file that is automatically created in App_Start when you install Ninject. I don't know if by having code in your Global file that means that Ninject hasn't set itself up correctly in your project?
Here is the code in NinjectWebCommon.cs:
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserRepository>().To<UserRepository>().InSingletonScope();
kernel.Bind<IUserManager>().To<UserManager>();
}
}
Here's the other difference I can see between our code, where I create the Kernel, my code declares two bindings to the kernel.
Here's the code for my test controller, I can set a breakpoint in the constructor and it gets it:
public class TestController : Controller
{
IUserManager _userManager;
public TestController(IUserManager userManager)
{
_userManager = userManager;
}
//
// GET: /Test/
public ActionResult Index()
{
return View();
}
}
This works with both my Controller and my APIControllers.

Nancy Ninject Module Construction

I'm trying to test my Nancy modules using Ninject as the IoC container. My problem is that I cannot seem to get Nancy to use my IoC bindings to resolve the NancyModule type.
I am using the latest Nancy on Nuget, the latest Nancy.Bootstrap.Ninject built from source with the latest Ninject.
My test setup is as follows:
[TestFixtureSetUp]
public virtual void ClassSetup()
{
Assembly.Load(typeof (MyModule).Assembly.FullName);
var bootstrapper = new AspHostConfigurationSource();
this.host = new Browser(bootstrapper);
}
[Test]
public void test()
{
/*snip */
var id = entity.Id;
var response = host.Put("/path/to/{0}/".With(id.Encode()),
(with) =>
{
with.HttpRequest();
with.Header("Accept", "application/xml");
});
response.StatusCode.Should().Be(Nancy.HttpStatusCode.OK);
/*snip */
}
That's my test setup, snipped. Now is my host setup (defined in my assembly):
public class MyBootstrapper: Nancy.Bootstrappers.Ninject.NinjectNancyBootstrapper
{
public bool ApplicationContainerConfigured { get; set; }
public Ninject.IKernel Container
{
get { return ApplicationContainer; }
}
public bool RequestContainerConfigured { get; set; }
protected override void ConfigureApplicationContainer(Ninject.IKernel existingContainer)
{
this.ApplicationContainerConfigured = true;
base.ConfigureApplicationContainer(existingContainer);
Nancy.Json.JsonSettings.MaxJsonLength = Int32.MaxValue;
StaticConfiguration.DisableCaches = true;
}
protected override void ConfigureRequestContainer(Ninject.IKernel container, NancyContext context)
{
container.Load(new[] { new ServiceModule() });
}
protected override DiagnosticsConfiguration DiagnosticsConfiguration
{
get { return new DiagnosticsConfiguration { Password = #"12345" }; }
}
}
My IoC bindings are as follows:
Bind<Nancy.NancyModule>()
.ToMethod(context =>
{
return new MyModule1(context.Kernel.Get<IMongoRepository<Guid, Entity>>(
Properties.Settings.Default.NamedCollection1),
context.Kernel.Get<IMongoRepository<Guid, Entity>>(
Properties.Settings.Default.NamedCollection2));
})
.Named(typeof(MyModule1).FullName);
Bind<Nancy.NancyModule>()
.ToMethod(context =>
{
return new MyModule2(context.Kernel.Get<IMongoRepository<Guid, Entity>>(
Properties.Settings.Default.NamedCollection3),
context.Kernel.Get<IMongoRepository<Guid, Entity>>(
Properties.Settings.Default.NamedCollection4));
})
.Named(typeof(MyModule2).FullName);
I want to take control of the construction on the Nancy modules. I've looked through the source for Nancy and it looks like for requests nancy asks the configured IoC container for all registered bindings for the type NancyModule with the appropriate key. Code that follows is defined in the Nancy.Bootstrappers.Ninject assembly
protected override sealed NancyModule GetModuleByKey(IKernel container, string moduleKey)
{
return container.Get<NancyModule>(moduleKey);
}
The key looks to be generated using a key generator object:
public class DefaultModuleKeyGenerator : IModuleKeyGenerator
{
/// <summary>
/// Returns a string key for the given type
/// </summary>
/// <param name="moduleType">NancyModule type</param>
/// <returns>String key</returns>
public string GetKeyForModuleType(Type moduleType)
{
return moduleType.FullName;
}
}
This is why my bindings are set up as named bindings. The idea is that when Nancy requests a named binding (for a module) it will pick p my named bindings.
It is not working as expected. Ninject is complaining that I have multiple bindings setup for the type NancyModule.
To reiterate: my goal is to take control of Nancy module construction.
Any ideas would be greatly appreciated.
P.S. When I talk about injecting dependecies into a module I do /not/ refer to NinjectModules as this question does

Categories

Resources