I am trying to create my own custom ViewEngine in MVC4 in order to do this I would like to inject a repository using ninject.
After reading several posts on stackoverflow and various blogs I came up with the code below:
NewViewEngine.cs
public class NewViewEngine: RazorViewEngine
{
[Inject]
private static IThemeRepository mThemeRepository { get; set; }
....
}
NinjectControllerFactory
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel(new NinjectSettings { UseReflectionBasedInjection = true });
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IThemeRepository>().To<EFThemeRepository>();
}
}
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ThemeViewEngine("web"));
}
However when I try to run this I get the following error:
Object reference not set to an instance of an object for mThemeRepository
so it would appear that my repository is in fact not getting injected and now I'm rather stumped, any ideas what the problem could be?
Related
tl;dr: I'm creating Type (by reflection) that is extending ApiController. How can I dynamically register it (it can be registered at startup; no need to do this at runtime).
Long story:
So in my application I have multiple interfaces, i.e.:
interface IMyInterface
{
MyResponse Hello(MyRequest request);
}
The thing that I want to achive is for each interface create controller that should look like this:
public class IMyInterfaceController : ApiController
{
public IMyInterface MyInterface { get; set; }
public MyResponse Hello([FromBody] MyRequest request)
{
return MyInterface.Hello(request);
}
}
Generating this controller is already done using heavy C# reflection. The thing is that I want to do right now is to register this controller under /api/{controller}/{action}.
In Global.asax right now I got this:
public class MvcApplication : System.Web.HttpApplication
{
private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
var controller = _reader.CreateController(new MyImplementation()); // MuImplementation implements IMyInterface
}
}
Solution with IHttpControllerFactory
I guess that what you need is a controller factory:
public class MyHttpControllerFactory : IHttpControllerFactory
{
private readonly InterfaceReader _reader;
private readonly HttpConfiguration _configuration;
public MyHttpControllerFactory(InterfaceReader reader, HttpConfiguration configuration)
{
_reader = reader;
_configuration = configuration;
}
public IHttpController CreateController(HttpControllerContext controllerContext, string controllerName)
{
if (controllerName == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", controllerContext.Request.RequestUri.AbsolutePath));
}
// Change the line below to whatever suits your needs.
var controller = _reader.CreateController(new MyImplementation());
controllerContext.Controller = controller;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(configuration, controllerName, controller.GetType());
return controllerContext.Controller;
}
public void ReleaseController(IHttpController controller)
{
// You may want to be able to release the controller as well.
}
}
Then in the Global.asax you need to register the custom controller factory:
public class MvcApplication : System.Web.HttpApplication
{
private readonly InterfaceReader _reader = new InterfaceReader(); // this class is doing all staff with reflection to create controller class
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.ServiceResolver.SetService(typeof(IHttpControllerFactory), new MyHttpControllerFactory(_reader, GlobalConfiguration.Configuration));
}
}
Solution with IHttpControllerActivator
If you use Web Api 2 then the solution is to use either IDependencyResolver or IHttpControllerActivator instead of the factory. I guess that the IHttpControllerActivator is better option in your case.
public class MyServiceActivator : IHttpControllerActivator
{
private readonly InterfaceReader _reader;
private readonly HttpConfiguration _configuration;
public MyServiceActivator(InterfaceReader reader, HttpConfiguration configuration)
{
_reader = reader;
_configuration = configuration;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
// Change the line below to whatever suits your needs.
var controller = _reader.CreateController(new MyImplementation());
return controller;
}
}
Then in the Global.asax you need to register the custom activator:
public class MvcApplication : System.Web.HttpApplication
{
// this class is doing all staff with reflection to create controller class
private readonly InterfaceReader _reader = new InterfaceReader();
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Services.Replace(typeof(IHttpControllerActivator), new MyServiceActivator(_reader, config));
}
}
I hope this helps.
You need to create your own class that implements IHttpControllerActivator
Here's an example that I use for DI with Windsor
public class WindsorCompositionRoot : IHttpControllerActivator
{
private readonly IWindsorContainer _container;
public WindsorCompositionRoot(IWindsorContainer container)
{
_container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController) _container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
}
Then you need to wire it up in the Global.asax in Application_Start()
Container = new WindsorContainer().Install(FromAssembly.This());
GlobalConfiguration.Configuration.Services.Replace(
typeof (IHttpControllerActivator),
new WindsorCompositionRoot(Container));
Installing the controllers is done in a Windsor Installer class
public class ControllersInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IHttpController>()
.LifestyleTransient());
}
}
I'm getting a null reference exception when hitting my code that tries to access the database.
This is in global.asax and I have stepped through the debugger and this code is being executed.
public class MvcApplication : System.Web.HttpApplication
{
public class DmsAppHost : AppHostBase
{
public DmsAppHost() : base("API", typeof(AppUsersService).Assembly) { }
public override void Configure(Funq.Container container)
{
var dbConnectionFactory = new OrmLiteConnectionFactory("Server=localhost;Port=5432;User Id=dms; Password=dms; Database=dms", PostgreSqlDialect.Provider);
container.Register<IDbConnectionFactory>(dbConnectionFactory);
container.RegisterAutoWiredAs<AppUserRepository, IAppUserRepository>();
}
}
protected void Application_Start()
{
new DmsAppHost().Init();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BootstrapSupport.BootstrapBundleConfig.RegisterBundles(System.Web.Optimization.BundleTable.Bundles);
}
}
When I hit this code (using (var db = DbConnectionFactory.OpenDbConnection())) I am getting a NullReferenceException.
Object reference not set to an instance of an object.
public class AppUserRepository : IAppUserRepository
{
public IDbConnectionFactory DbConnectionFactory { get; set; }
public AppUser GetAppUserByUsername(string username)
{
AppUser appUser;
using (var db = DbConnectionFactory.OpenDbConnection())
{
appUser = db.QuerySingle<AppUser>(new { username });
}
return appUser;
}
}
Am I to assume something is wrong with my Connection or is there something I'm not doing right to make Funq work?
You have shown some Funq container configuration that is injecting dependencies into your AppUserRepository class. But you haven't shown how and where is this class used. Since your question is tagged with asp.net-mvc I assume that this happens inside an ASP.NET MVC controller action. Something along the lines of:
public class HomeController: Controller
{
private readonly IAppUserRepository repo;
public HomeController(IAppUserRepository repo)
{
this.repo = repo;
}
public ActionResult Index(int id)
{
var model = this.repo.Get(id);
return View(model);
}
}
If you want to use a dependency injection framework in ASP.NET MVC you will have to write a custom dependency resolver which will do the injection into the controllers.
So let's go ahead and write one for Funq:
public class FunqDependencyResolver : IDependencyResolver
{
private readonly ContainerResolveCache cache;
public FunqDependencyResolver(Container container)
{
this.cache = new ContainerResolveCache(container);
var controllerTypes =
(from type in Assembly.GetCallingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(type)
select type).ToList();
container.RegisterAutoWiredTypes(controllerTypes);
}
public object GetService(Type serviceType)
{
return this.cache.CreateInstance(serviceType, true);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return Enumerable.Empty<object>();
}
}
and then we could register it in Application_Start:
protected void Application_Start()
{
var container = new Container();
var dbConnectionFactory = new OrmLiteConnectionFactory("Server=localhost;Port=5432;User Id=dms; Password=dms; Database=dms", PostgreSqlDialect.Provider);
container.Register<IDbConnectionFactory>(dbConnectionFactory);
container.RegisterAutoWiredAs<AppUserRepository, IAppUserRepository>();
DependencyResolver.SetResolver(new FunqDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BootstrapSupport.BootstrapBundleConfig.RegisterBundles(System.Web.Optimization.BundleTable.Bundles);
}
Also I would recommend you using constructor injection for your AppUserRepository class because the DbConnectionFactory is an absolutely required dependency for this class to work properly:
public class AppUserRepository : IAppUserRepository
{
private readonly IDbConnectionFactory dbConnectionFactory;
public class AppUserRepository(IDbConnectionFactory dbConnectionFactory)
{
this.dbConnectionFactory = dbConnectionFactory;
}
public AppUser GetAppUserByUsername(string username)
{
AppUser appUser;
using (var db = this.dbConnectionFactory.OpenDbConnection())
{
appUser = db.QuerySingle<AppUser>(new { username });
}
return appUser;
}
}
Tell the container how to resolve IAppUserRepository with the following configuration.
container.RegisterAutoWiredAs<AppUserRepository, IAppUserRepository>();
RegisterAutoWiredAs will inject the IDbConnectionFactory dependency
You're not showing how your resolve IAppUserRepository, but with Funq you would have to do any project injection in your factory delegate instance when you register the abstraction.
See http://funq.codeplex.com/discussions/217686 for more info.
your problem is that DbConnectionFactory doesn't becomes an object so it is still null
it's like you would do this
public TextBlock nullBlock; // it's your public IDbConnectionFactory DbConnectionFactory { get; set; }
nullBlock.Text; // DbConnectionFactory.OpenDbConnection()
you need to set an object
in your case something like
var obj = new AppUserRepository();
obj.DbConnectionFactory = .... //
additional if you fill it in some hidden code
you should do this and set a breakpoint maybe your overhanded object is null
private IDbConnectionFactory _dbConnectionFactory
public IDbConnectionFactory DbConnectionFactory
{
get {return _dbConnectionFactory; }
set {_dbConnectionFactory=value;}
}
So I went through the asp.net mvc tutorial for castle windsor, and my registrations look like:
private static IWindsorContainer _container = new WindsorContainer();
private static void BootstrapContainer()
{
_container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
protected void Application_End()
{
_container.Dispose();
}
So in my HomeController I have this:
public class HomeController : Controller
{
private IUserService _userService;
public HomeController(IUserService userService)
{
this._userService = userService;
}
}
How would I go about wiring this controller up to setup the IUserService?
Update
In case in matters how I need to wire things up, my vs.net projects are:
web, interfaces, entities, data (nhibernate), services
The implementation of WindsorControllerFactory should look like this from the doco http://docs.castleproject.org/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx?HL=ikernel.
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 exception. Can't resolve null type.
}
return (IController)kernel.Resolve(controllerType);
}
}
UPDATED
Each interface that needs to be resolved by dependancy injection need to be registered.
This can be done by calling the .Register method on the container.
container.Register(Component.For<IUserService>().ImplementedBy<UserService>().LifeStyle.Transient);
More info here: http://docs.castleproject.org/Windsor.Registering-components-one-by-one.ashx
I watched the first 2 beginner tutorials for Ninject on dimecasts.net. Now, I want to use Ninject 2.2 in ASP.NET MVC 3. I want a view with a mocked out Model. I get object reference not set to an instance of an object when calling my service;
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController()
{
}
HomeController(IMilestoneService service)
{
_service = service;
}
public ActionResult Index()
{
ViewBag.Message = "Change Request System";
return View();
}
public ActionResult About()
{
return View();
}
#region Partial views
public ActionResult Milestone()
{
var result = _service.GetMileStones();//OBJECT REF ERROR
return View(result);
}
#endregion
}
//####GLOBAL.ASAX
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up mvc, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//StartNinject();
}
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I'm using Razor, he's the milestone partial view
#foreach (var item in Model)
{
<div>item.Name</div>
}
Finally, the Home view Index
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
#Html.Action("Milestone");
</p>
Edit 11/20/2013
Note that Ninject has since released version 2.0. The changes are nicely outlined on their site. Of Note StandardModule is now NinjectModule and namespace Ninject.Core no longer exists. I was able to replace it with just Ninject.
There is an issue with your controller class, the constructor with the dependency is private. Your controller should look like:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
}
}
Don't even include a public parameterless constructor, it isn't even valid, your class needs that dependency to function.
In fact, I also insert a null check against that dependency in the constructor just to be sure my class is valid on construction:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
Enforce.NotNull(() => _service); // lambda to auto-magically get variable name for exception
}
}
There also may be an issue with your MvcApplication class.
Instead of protected void Application_Start(), there is a different function you can override, protected override void OnApplicationStarted()
This is where your calls to setup routing should go:
public class MvcApplication : NinjectHttpApplication
{
public override void Init()
{
base.Init();
Mappers.Initialize();
}
protected override Ninject.IKernel CreateKernel()
{
return Ioc.Initialize();
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
Routing.RegisterRoutes(routes);
//RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
}
Of course, if you are already calling Application_Start that's fine too, but I didn't see it in the OP.
Try this in your global.asax file:
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up ninject, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
//Your other stuff here. No need to call StartNinject().
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I believe if Ninject couldn't bind that interface, you'd get a binding error. This makes me think Ninject is not instantiating your controller.
Have you included Ninject.Web.Mvc?
I've just started playing with IoC containers and therefore chosed Ninject.
After several hours of sweat and tears I still cant figure out how to setup my MVC3 application with Ninject.
So far I have:
Global.asax.cs
public class MvcApplication : Ninject.Web.Mvc.NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
DependencyResolver.SetResolver(new MyDependencyResolver(CreateKernel()));
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new [] { new ServiceModule() };
return new StandardKernel(modules);
}
}
ServiceModule.cs
internal class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IGreetingService>().To<GreetingService>();
}
}
MyDependencyResolver.cs
public class MyDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public MyDependencyResolver(IKernel kernel)
{
this.kernel = kernel;
}
public object GetService(System.Type serviceType)
{
return kernel.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType)
{
return kernel.GetAll(serviceType);
}
}
GreetingService.cs
public interface IGreetingService
{
string Hello();
}
public class GreetingService : IGreetingService
{
public string Hello()
{
return "Hello from GreetingService";
}
}
TestController.cs
public class TestController : Controller
{
private readonly IGreetingService service;
public TestController(IGreetingService service)
{
this.service = service;
}
public ActionResult Index()
{
return View("Index", service.Hello());
}
}
Each time I try to load the Index view it either just throws a overflow exception or a HTTP 404 error - If I remove all the Ninject code it works perfectly, whats wrong?
You are mixing an own dependency resolver with the MVC extension. I'd suggest either going with your own dependency resolver or with using the MVC extension but not both. When using the MVC extension you have to use OnApplicationStarted instead of Application_Start.
See http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/ and have a look at the SampleApplication that comes with the source code of the MVC extension https://github.com/ninject/ninject.web.mvc.
Also the fix is not used anymore when you use the current version for the build server: http://teamcity.codebetter.com
UPDATE: The Ninject.MVC3 package continues to be updated and works OOTB against MVC4 RTM (and RC). See this page in the wiki for details.