Castle Windsor - Injecting IActionInvoker Implementation Issue - c#

I am trying to use the approach from this article, but I am missing something - I am currently getting an error within WindsorControllerFactory.GetControllerInstance when it tries to resolve IActionInvoker since WindsorActionInvoker has a dependency on IWindsorContainer.
Given that WindsorControllerFactory already has a reference to IWindsorContainer, could I pass that reference in? If so - how? The only examples I have found are about passing value types as constructor parameters, not reference types.
I'm feeling I'm missing something obvious...
Current setup as follows:
Within Global.asax Application_Start I call the following method:
protected virtual IWindsorContainer InitializeServiceLocator()
{
IWindsorContainer container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
container.RegisterControllers(typeof(HomeController).Assembly);
ComponentRegistrar.AddComponentsTo(container);
return container;
}
ComponentRegistrar:
public static void AddComponentsTo(IWindsorContainer container)
{
//add other components.....
container.AddComponentLifeStyle<IActionInvoker, WindsorActionInvoker>(LifestyleType.PerWebRequest);
}
WindsorActionInvoker:
public class WindsorActionInvoker : ControllerActionInvoker, IActionInvoker
{
readonly IWindsorContainer container;
public WindsorActionInvoker(IWindsorContainer container)
{
this.container = container;
}
protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters)
{
foreach (IActionFilter actionFilter in filters)
{
container.Kernel.InjectProperties(actionFilter);
}
return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
}
}
WindsorControllerFactory:
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IWindsorContainer container;
public WindsorControllerFactory(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public override void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
this.container.Release(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return base.GetControllerInstance(requestContext, controllerType);
}
var controller = this.container.Resolve(controllerType) as Controller;
if (controller != null)
{
controller.ActionInvoker = this.container.Resolve<IActionInvoker>(this.container);
}
return controller;
}
}
Update
I missed a subtlety: I was trying to use this behaviour for the following:
public class CustomAuthorize : AuthorizeAttribute {...}
which doesn't implement IActionFilter.
Added the following to WindsorActionInvoker:
protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
foreach (IAuthorizationFilter authorizeFilter in filters)
{
this.kernel.InjectProperties(authorizeFilter);
}
return base.InvokeAuthorizationFilters(controllerContext, filters, actionDescriptor);
}
This now works as required. Thanks to Cristiano since it was analysis of his kindly provided code which put me on the right path.

Global.asax
private static void bootstrapContainer()
{
container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
Installer / filling container
public class ControllersInstaller : IWindsorInstaller
{
#region IWindsorInstaller Members
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<WpRegistration.Web.Filters.AgencyAuthorize>().LifeStyle.Transient);
container.Register(Component.For<IActionInvoker>().ImplementedBy<WindsorExtensions.Mvc.WindsorActionInvoker>().LifeStyle.Transient);
container.Register(AllTypes.FromThisAssembly()
.BasedOn<IController>()
.If(Component.IsInSameNamespaceAs<HomeController>())
.If(t => t.Name.EndsWith("Controller"))
.Configure((c => c.LifeStyle.Transient)));
}
#endregion
}
WindsorControllerFactory
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Castle.MicroKernel;
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel kernel;
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
}
public override void ReleaseController(IController controller)
{
kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
IController iController = (IController)kernel.Resolve(controllerType);
// new code
if (iController is Controller)
{
((Controller)iController).ActionInvoker = kernel.Resolve<IActionInvoker>();
}
return iController;
}
}
WindsorActionInvoker
namespace WindsorExtensions.Mvc
{
public class WindsorActionInvoker : ControllerActionInvoker
{
readonly IKernel kernel;
public WindsorActionInvoker(IKernel kernel)
{
this.kernel = kernel;
}
protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext
, IList<IActionFilter> filters
, ActionDescriptor actionDescriptor
, IDictionary<string, object> parameters)
{
foreach (IActionFilter actionFilter in filters)
{
kernel.InjectProperties(actionFilter);
}
return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
}
}
public static class WindsorExtension
{
public static void InjectProperties(this IKernel kernel, object target)
{
var type = target.GetType();
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.CanWrite && kernel.HasComponent(property.PropertyType))
{
var value = kernel.Resolve(property.PropertyType);
try { property.SetValue(target, value, null); }
catch (Exception ex)
{
var message = string.Format("Error setting property {0} on type {1}, See inner exception for more information.", property.Name, type.FullName);
throw new ComponentActivatorException(message, ex);
}
}
}
}
}
}
AgencyAuthorizeAttribute
namespace WpRegistration.Web.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class AgencyAuthorize : ActionFilterAttribute
{
CurrentUserService _currentUserSvc;
public AgencyAuthorize() { }
public CurrentUserService Service
{
get { return _currentUserSvc; }
set
{
this._currentUserSvc=value;
}
}

Related

StructureMap Custom ActionFilter

I am using Asp.Net MVC. I have custom AcitonFilter using structuremap.
In custom ActionFilter I use an enum type.
public enum TimeUnit
{
Minute = 60,
Hour = 3600,
Day = 86400
}
And my ActionFilter class is:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CustomAttribute : ActionFilterAttribute
{
public TimeUnit Time { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var seconds = Convert.ToInt32(Time);
}
}
And my structuremap's settings for ActionFilter is:
public class StructurMapFilterProvider : FilterAttributeFilterProvider
{
private readonly Func<StructureMap.IContainer> _Container;
public StructurMapFilterProvider(Func<StructureMap.IContainer> container)
{
_Container = container;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
var container = _Container();
foreach (var filter in filters)
{
container.BuildUp(filter.Instance);
yield return filter;
}
}
}
After I use ActionFilter in action throw an Exception in
container.BuildUp(filter.Instance);
Message of Exception is:
Setter Property 'Time' (ActionFilters.TimeUnit): Required primitive dependency is not explicitly defined
I used like this:
[Custom(Time = TimeUnit.Hour)]
public ActionResult MyAction(ViewModel model)
{
// my code
}
What should I do?
How explicitly define a enumeration?

ASP.NET MVC 6 Controller Factory

I want to create controllers action from database (ASP.NET MVC 6 vNext). I have table controller and also actions
action table has properties { ViewPath, ActionName } Where actionName is {Controller}/{ActionName}
I want build pages like this. How can i make it?
I have class for MVC 4 but I need to rewrite it to MVC 6
public class ITSDefaultController : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
try
{
return base.CreateController(requestContext, controllerName) as Controller;
}
catch (Exception)
{
Controller controller = new ITSControllerBase();
using (var db = new ITS.Database.DatabaseDataContext())
{
string action = requestContext.RouteData.Values["action"] as string;
DynamicAction dynamicAction = null;
if (!db.Controllers.Any(x => x.ControllerName == controllerName && x.Views.Any(v => v.ViewName == action)))
{
dynamicAction = Actions["NotFound"].First();
requestContext.RouteData.Values["controller"] = "NotFound";
requestContext.RouteData.Values["action"] = "Index";
}
else
{
dynamicAction = new DynamicAction()
{
ActionName = db.Views.First(d => d.ViewName == action && d.Controller.ControllerName == controllerName).ViewName,
Result = () => new ViewResult()
};
}
if (dynamicAction != null)
{
controller.ActionInvoker = new DynamicActionInvoker() { DynamicAction = dynamicAction };
}
return controller;
}
}
}
public override void ReleaseController(IController controller)
{
base.ReleaseController(controller);
}
public static ConcurrentDictionary> Actions = new ConcurrentDictionary>();
}
actually I have the same need to replace Mvc pipeline components by some custom ones, and I found that the IControllerFactory and IControllerActivator and their default implementations still the same, then the experience was to replace the Mvc 6 DefaultControllerFactory by the CustomControllerFactory, I've done some tests on the startup class on ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var serviceDescriptor = services.FirstOrDefault(s => s.ServiceType.FullName.Contains("IControllerFactory"));
var serviceIndex = services.IndexOf(serviceDescriptor);
services.Insert(serviceIndex, new ServiceDescriptor(typeof(IControllerFactory), typeof(CustomControllerFactory), ServiceLifeTime.Singleton));
services.RemoveAt(serviceIndex + 1);
}
and it s done, also you can add an extension method to the IServiceCollection interface:
public static class ServiceCollectionExtensions
{
public static void(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime serviceLifetime = ServiceLifetime.Singleton)
{
var serviceDescriptor = services.FirstOrDefault(s => s.ServiceType == serviceType);
var serviceIndex = services.IndexOf(serviceDescriptor);
services.Insert(serviceIndex, new ServiceDescriptor(serviceType, implementationType, serviceLifetime));
services.RemoveAt(serviceIndex + 1);
}
}
after this modification you can use it as simple as this :
......
services.AddMvc();
services.ReplaceService(typeof(IControllerActivator), typeof(CustomControllerActivator));
services.ReplaceService(typeof(IControllerFactory), typeof(CustomControllerFactory));
......
then you can replace any component on the Mvc 6 Pipeline;
public class HomeController : Controller
{
public string _MyName { get; set; }
// GET: Home
public ActionResult Index()
{
return Content(_MyName);
}
public HomeController(string Name)
{
_MyName = Name;
}
}
public class MyCustomController : IControllerFactory
{
public IController CreateController(RequestContext requestContext, string controllerName)
{
HomeController objHomeController = new HomeController("Any thing Which you want to pass/inject.");
return objHomeController;
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if(disposable!=null)
{
disposable.Dispose();
}
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new MyCustomController());
}

Unity Container for holding User Session objects MVC

I'm buold MVC4 C# website.
I have base controller with constructor
public BaseController(IUnityContainer container, SessionContext context)
The problem is that container is shared globally and when i try to resolve or register objects they are shared through sessions. On the other hand SessionContext (parameter) comes only is unique to user session. I want container to be unique for user sessions (to be able resolve unique objects for user sessions), but i don't know how to implement it.
I have UnityControllerFactory as follows:
public class UnityControllerFactory : DefaultControllerFactory
{
private IUnityContainer container;
public UnityControllerFactory(IUnityContainer container){
this.container = container;
this.RegisterTypes();
}
protected override IController GetControllerInstance(RequestContext context, Type controllerType)
{
if (controllerType != null)
{
return this.container.Resolve(controllerType) as IController;
}
return null;
}
private void RegisterTypes(){
container.RegisterType<SessionContext>(new UnityPerSessionLifetimeManager("private.SessionContext"));
container.RegisterType<DataService>(new UnityPerSessionLifetimeManager("private.Service"));
}
}
and lifetime manager
public class UnityPerSessionLifetimeManager : LifetimeManager
{
private string sessionKey;
public UnityPerSessionLifetimeManager(string sessionKey)
{
this.sessionKey = sessionKey;
}
public override object GetValue()
{
return HttpContext.Current.Session[this.sessionKey];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(this.sessionKey);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[this.sessionKey] = newValue;
}
}

Getting object reference error when using setter injection for ActionFilter [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
I've created custom action filter attribute for logging error and save logs into database:
public class LogAttribute : ActionFilterAttribute
{
public string Description { get; set; }
public IUnitOfWork Uow { get; set; }
public ILogActionService LogActionService { get; set; }
public IApplicationUserManager ApplicationUserManager { get; set; }
public LogAttribute(string desciption)
{
Description = desciption;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//var userId = filterContext.HttpContext.User.Identity.GetUserId();
//var user = await ApplicationUserManager.FindByIdAsync(2);
var model = new LogAction(filterContext.ActionDescriptor.ActionName,
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, Description);
LogActionService.AddLog(model);
Uow.SaveAllChanges();
base.OnActionExecuted(filterContext);
}
}
So, I also created custom filter provider for injecting my dependency using StructureMap:
public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
private Func<IContainer> _container;
public StructureMapFilterProvider(Func<IContainer> container)
{
_container = container;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
var container = _container();
foreach (var filter in filters)
{
container.BuildUp(filter.Instance);
yield return filter;
}
}
}
Then I've registered that in Global.asax:
cfg.For<IFilterProvider>().Use(new StructureMapFilterProvider(() => SmObjectFactory.Container));
cfg.Policies.SetAllProperties(x => x.Matching(p => p.DeclaringType.CanBeCastTo(typeof(ActionFilterAttribute)) &&
p.DeclaringType.Namespace.StartsWith("MyNamespace") &&
!p.PropertyType.IsPrimitive &&
p.PropertyType != typeof(string)));
Everything should be ok, But when I decorate my action method with Log attribute, I get Object reference not set to an instance of an object. I set break point in OnActionExecuted it tells that LogActionService is null:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//var userId = filterContext.HttpContext.User.Identity.GetUserId();
//var user = await ApplicationUserManager.FindByIdAsync(2);
var model = new LogAction(filterContext.ActionDescriptor.ActionName,
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, Description);
LogActionService.AddLog(model);
Uow.SaveAllChanges();
base.OnActionExecuted(filterContext);
}
Defining StructureMapFilterProvider to setup property injection:
using StructureMap;
using System.Collections.Generic;
using System.Web.Mvc;
namespace DI06.CustomFilters
{
public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
private readonly IContainer _container;
public StructureMapFilterProvider(IContainer container)
{
_container = container;
}
public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var filters = base.GetFilters(controllerContext, actionDescriptor);
foreach (var filter in filters)
{
_container.BuildUp(filter.Instance);
yield return filter;
}
}
}
}
Creating our custom StructureMap Container:
using System;
using System.Threading;
using DI06.Services;
using DI06.Services.Contracts;
using StructureMap;
namespace DI06.IocConfig
{
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(x =>
{
x.For<ILogActionService>().Use<LogActionService>();
x.Policies.SetAllProperties(y =>
{
y.OfType<ILogActionService>();
});
});
}
}
}
And then it's necessary to remove the old FilterAttributeFilterProvider and replace it with the new StructureMapFilterProvider (in Application_Start method):
var filterProvider = FilterProviders.Providers.Single(provider => provider is FilterAttributeFilterProvider);
FilterProviders.Providers.Remove(filterProvider);
FilterProviders.Providers.Add(SmObjectFactory.Container.GetInstance<StructureMapFilterProvider>());

Controller does not have a default constructor 500 internal server error

This is my controller
public class SuggestionController : ApiController
{
public ISuggestionRepository Repository { get; private set; }
public SuggestionController(ISuggestionRepository repository)
{
this.Repository = repository;
}
// to post suggestion
[HttpPost]
[ActionName("PostSuggestion")]
public HttpResponseMessage PostSuggestion(Suggestion suggestion)
{
var answerCorrect = this.Repository.CreateSuggestion(suggestion);
if (answerCorrect == true)
return Request.CreateResponse(HttpStatusCode.OK);
else
return Request.CreateResponse(HttpStatusCode.Conflict);
}
}
and this is my RegisterServices method in NinjectWebCommon.cs
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
kernel.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
kernel.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
kernel.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
But am getting an exception that my suggestion controller does not have a default constructor and its showing a 500 internal server when am hitting the controller from a client app
I know that we get the exception of controller not having default constructor if the ninject dependency is not working properly but the below is another controller i have implemeneted similar to suggestion controller and its working absolutely fine.
public IUserRepository Repository { get; private set; }
public SSOController(IUserRepository repository)
{
this.Repository = repository;
}
[HttpPost]
[ActionName("PostUser")]
public HttpResponseMessage PostUser([FromBody]string id)
{
var accessToken = id;
var client = new FacebookClient(accessToken);
dynamic result = client.Get("me", new { fields = "name,email" });
string name = result.name;
string email = result.email;
var existingUser = this.Repository.FindByUserIdentity(name);
if (existingUser == null)
{
var newUser = new User
{
Username = name,
Email = email,
};
var success = this.Repository.CreateAccount(newUser);
if (!success)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
//return created status code as we created the user
return Request.CreateResponse<User>(HttpStatusCode.Created, newUser);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
I have no idea where am going wrong. Please let me know if u have any suggestions.
EDIT:
my Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AuthConfig.RegisterAuth();
GlobalConfiguration.Configuration.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
}
Dependency resolver am using
// Provides a Ninject implementation of IDependencyScope
// which resolves services using the Ninject container.
public class NinjectDependencyScope : IDependencyScope
{
IResolutionRoot resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.GetAll(serviceType);
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
}
// This class is the resolver, but it is also the global scope
// so we derive from NinjectScope.
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
and calling it in CreateKernel() method in NinjectWebCommon
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
// Install our Ninject-based IDependencyResolver into the Web API config
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
Suggestion Repository
public class SuggestionRepository : Repository<Suggestion>, ISuggestionRepository
{
public SuggestionRepository(IServiceContext<Suggestion> servicecontext)
: base(servicecontext)
{ }
public bool CreateSuggestion(Suggestion suggestion)
{
this.ServiceContext.Create(suggestion);
this.ServiceContext.Save();
return true;
}
}
ISuggestionRepository
public interface ISuggestionRepository
{
bool CreateSuggestion(Suggestion suggestion);
}
Repository
public abstract class Repository<T>
{
public IServiceContext<T> ServiceContext { get; private set; }
public Repository(IServiceContext<T> serviceContext)
{
this.ServiceContext = serviceContext;
}
}
IserviceContext
public interface IServiceContext<T>
{
IQueryable<T> QueryableEntities { get; }
void Create(T entity);
void Update(T entity);
void Delete(T entity);
void Save();
}
Since you're using WebApi, you will need to use the WebApi extension for Ninject. Unfortunately, the current Ninject.WebApi nuget package is out of date, and doesn't work with the released version of WebApi.
Temporarily, until Remo gets around to updating Ninject.WebApi to the release version, you can use Ninject.WebApi-RC http://nuget.org/packages/Ninject.Web.WebApi-RC
http://www.eyecatch.no/blog/2012/06/using-ninject-with-webapi-rc/
EDIT:
To recap the information discussed in comments, Here are the recommendations:
1) Use Ninject.MVC3 and Ninject.Web.WebApi (but use Ninject.Web.WebApi-RC until the official is updated) as discussed above. Do not use a custom DependencyResolver, and let Ninject.Web.Mvc and .WebApi do their job.
2) Change your bindings to this:
kernel.Bind<ICompetitionRepository>().To<CompetitionRepository>();
... similar bindings
3) Add a generic binding for your ServiceContext
kernel.Bind(typeof(IServiceContext<>)).To(typeof(InMemoryDataContext<>));
I think the problem is you're using the ApiController.
Controllers and apiControllers are using a different dependancy injection container.
Both of them however expose the same methods.
If the working controller is inheriting the Controller class then that's your cause.
For a work around take a look at
this topic
I have faced the same issue.
This is how I rectified:
I created a WebContainerManager which is just a static wrapper around container.
Static container wrappers useful when you don't control instantiation and can't rely on injection - e.g. action filter attributes
public static class WebContainerManager
{
public static IKernel GetContainer()
{
var resolver = GlobalConfiguration.Configuration.DependencyResolver as NinjectDependencyResolver;
if (resolver != null)
{
return resolver.Container;
}
throw new InvalidOperationException("NinjectDependencyResolver not being used as the MVC dependency resolver");
}
public static T Get<T>()
{
return GetContainer().Get<T>();
}
}
Inside your controller, call your empty constructor like this with no parameters:
public SuggestionController() : this(WebContainerManager.Get<ISuggestionRepository>())
{
}
This should work.
This technique i got from the book on MVC4 by Jamie Kurtz #jakurtz.
You probably need to do some dependency injection so you can inject the ISuggestionRepository parameter on your SuggestionController constructor. To do that you need to override methods in the DefaultControllerFactory class to customize the creation of controllers. Since you are using NInject, you can have something like:
public class NInjectControllerFactory : DefaultControllerFactory
{
private IKernel kernel = new StandardKernel(new CustomModule());
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)kernel.Get(controllerType);
}
public class CustomModule : NinjectModule
{
public override void Load()
{
this.Bind<ICompetitionRepository>().To(typeof(CompetitionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Competition>());
this.Bind<ISubmissionRepository>().To(typeof(SubmissionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Submission>());
this.Bind<IUserRepository>().To(typeof(UserRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<User>());
this.Bind<ISuggestionRepository>().To(typeof(SuggestionRepository))
.WithConstructorArgument("serviceContext", new InMemoryDataContext<Suggestion>());
}
}
}
Then in your Global.asax.cs, you can add a line to swap out the controller factory
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new NInjectControllerFactory());
}

Categories