Ninject in MVC and WebAPI to create abstract factory - c#

I am using Ninject in a C# 4.5 project as the IoC container. I am trying to use Ninject to inject into an abstract factory pattern which works fine in MVC but not in Web Api.
I have the following installed:
Ninject 3.0.1.10
Ninject.Extensions.WCF 3.0.0.5
Ninject.MVC3 3.0.0.6
Ninject.Web.Common 3.0.0.7
SignalR.Ninject
My factory classes look like this:
public interface IBusinessEngineFactory
{
T GetBusinessEngine<T>() where T : IBusinessEngine;
}
public interface IBusinessEngine
{
}
public class BusinessEngineFactory : IBusinessEngineFactory
{
private IKernel Kernel { get; set; }
public BusinessEngineFactory(IKernel kernel)
{
Kernel = kernel;
}
T IBusinessEngineFactory.GetBusinessEngine<T>()
{
return Kernel.Get<T>();
}
}
I have the standard NinjectWebCommon:
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);
// Install our Ninject-based IDependencyResolver into the Web API config
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<IUserIdentity>().To<UserIdentity>();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
kernel.Bind<IAlarmEngine>().To<AlarmEngine>();
kernel.Bind<IBusinessEngineFactory>().To<BusinessEngineFactory>();
}
}
This is set up in a controller thus
public class UserController : BaseController
{
public UserController(IBusinessEngineFactory businessEngineFactory, IUnitOfWork unitOfWork, UserIdentity userIdentity)
: base(businessEngineFactory, unitOfWork, userIdentity)
{
}
...
public ActionResult Edit(int id = 0)
{
...
var x = BusinessEngineFactory.GetBusinessEngine<IAlarmEngine>();
x.ProcessAlarmEventUpdate("test");
return View(x);
}
}
Which works fine. Where I have the problems is identical code in the WebAPIController:
public class AlarmWebApiController : ApiController
{
public IBusinessEngineFactory BusinessEngineFactory { get; set; }
public IUnitOfWork UnitOfWork {get; set;}
public IUserIdentity UserIdentity { get; set; }
public AlarmWebApiController(IBusinessEngineFactory businessEngineFactory, IUnitOfWork unitOfWork, IUserIdentity userIdentity)
{
if (businessEngineFactory == null) throw new NullReferenceException("businessEngineFactory");
BusinessEngineFactory = businessEngineFactory;
if (unitOfWork == null) throw new NullReferenceException("unitOfWork");
UnitOfWork = unitOfWork;
if (userIdentity == null) throw new NullReferenceException("userIdentity");
UserIdentity = userIdentity;
}
}
When I run this the MVC controller processes fine but the Web API function does not and I get the obscure error message which occurs before any code in the constructor of the webapi is called.
"Error loading Ninject component ICache
No component has been registered in the kernel's component container"
I think the problem is the that MVC has knowledge of Kernel but because WebAPI uses this:
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
so it does not know the kernel. The code for creating the business engine exists in a different project to the main MVC, Web API and ninject creation and I would prefer not to stick a dependency to System.Web.Http (for the GlobalConfiguration.Configuration.DependencyResolver object) if I can help it. Is there any way to make it work across both MVC and WebAPI?
Thanks in advance

I fixed this problem so the IoC would inject the abstract factory and return the correct concrete implementation in both MVC and WebAPI. In the end my solution had the following packages:
<package id="Ninject" version="3.0.1.10" targetFramework="net40" />
<package id="Ninject.Extensions.Wcf" version="3.0.0.5" targetFramework="net40" />
<package id="Ninject.MVC3" version="3.0.0.6" targetFramework="net40" />
<package id="Ninject.Web.Common" version="3.0.0.7" targetFramework="net40" />
<package id="Ninject.Web.WebApi-RC" version="3.0.0.22" targetFramework="net45" />
The business engine factory was modified to use the following:
public class BusinessEngineFactory : IBusinessEngineFactory
{
private IResolutionRoot resolutionRoot { get; set; }
public BusinessEngineFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
T IBusinessEngineFactory.GetBusinessEngine<T>()
{
return this.resolutionRoot.Get<T>();
}
}
I tried to use the package Ninject.Web.WebApi which didn't work and Ninject.Web.WeApi.Updated which stopped my solution from running with a "exception has been thrown by the target of an invocation" error message on application start. Going back to the older version was the solution so now everything is working fine. I even checked it worked in WCF as well!

Related

How to share ServiceProvider in Maui Blazor for Maui services and Blazor services

I am trying to figure out how to share the services between Maui and Blazor in a MauiBlazor project.
The goal is to be able to consume the same singletons without having a static instance of these singletons stored somewhere.
While it may be a more convenient way in the future releases, I found a way to do that with some inspirations from Maui.Plugins.PageResolver. Related to the issue maui#792
I have added a nuget + working sample app on Github
Here is how I solved it:
1. Create a repository for the service provider
public static class Resolver
{
private static IServiceProvider _serviceProvider;
public static IServiceProvider ServiceProvider => _serviceProvider ?? throw new Exception("Service provider has not been initialized");
/// <summary>
/// Register the service provider
/// </summary>
public static void RegisterServiceProvider(IServiceProvider sp)
{
_serviceProvider = sp;
}
/// <summary>
/// Get service of type <typeparamref name="T"/> from the service provider.
/// </summary>
public static T Resolve<T>() where T : class
=> ServiceProvider.GetRequiredService<T>();
}
2. An extension method for the app startup
public static void UseResolver(this MauiApp app)
{
Resolver.RegisterServiceProvider(app.Services);
}
Or to avoid dependency to Maui:
public static void UseResolver(this IServiceProvider sp)
{
Resolver.RegisterServiceProvider(sp);
}
3a. A sample test service
public class TestSingletonService
{
private static int _staticIndex = 0;
public int Index { get; set; }
public TestSingletonService()
{
Index = _staticIndex++;
}
}
3b. Register the service collection
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.RegisterBlazorMauiWebView()
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddBlazorWebView();
builder.Services.AddSingleton<WeatherForecastService>();
// Register any other service / ViewModel / Page here
builder.Services.AddSingleton<TestSingletonService>();
// Pre-build the app
var app = builder.Build();
// Intercept and register the ServiceProvider
app.UseResolver();
// Or to avoid Maui dependency
// app.Services.UseResolver();
// Return the app as usual
return app;
}
}
4a. Use it as your factory from Maui side
Resolver.ServiceProvider.GetRequiredService<TestSingletonService>();
Console.WriteLine($"Instance number {TestSingletonService.Index}");
4b. The services instances will be shared with your Blazor #inject services.
#inject TestSingletonService tester
Instance number #(tester.Index).

NopCommerce 4.20 Plugin development error with Dependency Injection

I have a NopCommerce Plugin development with the dBContext name BookAppointmentDBContext and Dependency Registrar DependencyRegistrar see my snippet below.
public class DependencyRegistrar : IDependencyRegistrar
{
private const string CONTEXT_NAME ="nop_object_context_bookappointment";
public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
{
builder.RegisterType<BookAppointmentService>().As<IBookAppointmentService>().InstancePerLifetimeScope();
//data context
builder.RegisterPluginDataContext<BookAppointmentDBContext>(CONTEXT_NAME);
//override required repository with our custom context
builder.RegisterType<EfRepository<CarInspectionModel>>()
.As<IRepository<CarInspectionModel>>()
.WithParameter(ResolvedParameter.ForNamed<IDbContext>(CONTEXT_NAME))
.InstancePerLifetimeScope();
}
public int Order => 1;
}
and BookAppointmentDBContext class below
public class BookAppointmentDBContext : DbContext, IDbContext
{
#region Ctor
public BookAppointmentDBContext(DbContextOptions<BookAppointmentDBContext> options) : base(options)
{
}
/*the other implementation of IDbContext as found in http://docs.nopcommerce.com/display/en/Plugin+with+data+access*/
}
Also, I have a BasePluglin class with
public class BookAppointmentPlugin : BasePlugin
{
private IWebHelper _webHelper;
private readonly BookAppointmentDBContext _context;
public BookAppointmentPlugin(IWebHelper webHelper, BookAppointmentDBContext context)
{
_webHelper = webHelper;
_context = context;
}
public override void Install()
{
_context.Install();
base.Install();
}
public override void Uninstall()
{
_context.Uninstall();
base.Uninstall();
}
}
I keep having this error:
ComponentNotRegisteredException: The requested service 'Microsoft.EntityFrameworkCore.DbContextOption 1[[Nop.Plugin.Misc.BookAppointment.Models.BookAppointmentDBContext, Nop.Plugin.Misc.BookAppointment, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
I have BookAppointmentDBContext registered but the error state otherwise.
Any idea what I did wrongly?
This issue is the lack of a registered DbContextOption which is part of the constructor needed to initialize the target db context.
Internally this is what RegisterPluginDataContext does.
/// <summary>
/// Represents extensions of Autofac ContainerBuilder
/// </summary>
public static class ContainerBuilderExtensions
{
/// <summary>
/// Register data context for a plugin
/// </summary>
/// <typeparam name="TContext">DB Context type</typeparam>
/// <param name="builder">Builder</param>
/// <param name="contextName">Context name</param>
public static void RegisterPluginDataContext<TContext>(this ContainerBuilder builder, string contextName) where TContext : DbContext, IDbContext
{
//register named context
builder.Register(context => (IDbContext)Activator.CreateInstance(typeof(TContext), new[] { context.Resolve<DbContextOptions<TContext>>() }))
.Named<IDbContext>(contextName).InstancePerLifetimeScope();
}
}
Source
Note it is trying to resolve DbContextOptions<TContext> when activating the context.
You would need to build the db context options and provide it to the container so that it can be injected into the context when being resolved.
private const string CONTEXT_NAME ="nop_object_context_bookappointment";
public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config) {
//...code removed for brevity
var optionsBuilder = new DbContextOptionsBuilder<BookAppointmentDBContext>();
optionsBuilder.UseSqlServer(connectionStringHere);
DbContextOptions<BookAppointmentDBContext> options = optionsBuilder.Options;
builder.RegisterInstance<DbContextOptions<BookAppointmentDBContext>>(options);
//data context
builder.RegisterPluginDataContext<BookAppointmentDBContext>(CONTEXT_NAME);
//...code removed for brevity
}
Reference Configuring a DbContext

How to create dependency injection for ASP.NET MVC 5?

Creating Dependency Injection with ASP.NET Core is fairly easy. The documentation explains it very well here and this guy has a killer video to explain it.
However, I want to do the same thing with my ASP.NET MVC 5 project. How can handle dependency injection with ASP.MVC 5?
Also, is Dependency injection limited to controllers only or can it work with any class?
In ASP.Net MVC you can use the .Net Core DI from NuGet rather than one of the third-party alternatives:-
using Microsoft.Extensions.DependencyInjection
For the MVC Start/Configuration class:-
public void Configuration(IAppBuilder app)
{
// We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
var services = new ServiceCollection();
// configure all of the services required for DI
ConfigureServices(services);
// Configure authentication
ConfigureAuth(app);
// Create a new resolver from our own default implementation
var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
// Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
//Other services may be added elsewhere through time
DependencyResolver.SetResolver(resolver);
}
My project uses Identity User and I've replaced the OWIN start-up configuration to follow a service-based approach instead. The default Identity User classes use static factory methods to create instances. I've moved that code into the constructors and relied on DI to provide the appropriate injection. It is still work in progress but here is where I am at:-
public void ConfigureServices(IServiceCollection services)
{
//====================================================
// Create the DB context for the IDENTITY database
//====================================================
// Add a database context - this can be instantiated with no parameters
services.AddTransient(typeof(ApplicationDbContext));
//====================================================
// ApplicationUserManager
//====================================================
// instantiation requires the following instance of the Identity database
services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));
// with the above defined, we can add the user manager class as a type
services.AddTransient(typeof(ApplicationUserManager));
//====================================================
// ApplicationSignInManager
//====================================================
// instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
services.AddTransient(typeof(ApplicationSignInManager));
//====================================================
// ApplicationRoleManager
//====================================================
// Maps the rolemanager of identity role to the concrete role manager type
services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();
// Maps the role store role to the implemented type
services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
services.AddTransient(typeof(ApplicationRoleManager));
//====================================================
// Add all controllers as services
//====================================================
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
}
The Account Controller class has the single constructor:-
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private RoleManager<IdentityRole> _roleManager;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
{
UserManager = userManager;
SignInManager = signInManager;
RoleManager = roleManager;
}
}
My Default Dependency Resolver:
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
For this answer I downloaded a Microsoft Example of WebApi project as a basis for the example and added DI services to it as follows,
Update the Target Framework to 4.6.1
NuGet the DI package :- Microsoft.Extensions.DependencyInjection
After the standard MapHttpRoute configuration, add code to register which services you need
using's
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// create the DI services and make the default resolver
var services = new ServiceCollection();
services.AddTransient(typeof(DefaultProduct));
services.AddTransient(typeof(ProductsController));
var resolver = new MyDependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
DefaultProduct
public class DefaultProduct : ProductsApp.Models.Product
{
public DefaultProduct()
{
this.Category = "Computing";
this.Id = 999;
this.Name = "Direct Injection";
this.Price = 99.99M;
}
}
MyDependencyResolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods.
/// This is combined dependency resolver for MVC and WebAPI usage.
/// </summary>
public class MyDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
protected IServiceProvider serviceProvider;
protected IServiceScope scope = null;
public MyDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public MyDependencyResolver(IServiceScope scope)
{
this.scope = scope;
this.serviceProvider = scope.ServiceProvider;
}
public IDependencyScope BeginScope()
{
return new MyDependencyResolver(serviceProvider.CreateScope());
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
scope?.Dispose();
}
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
ServiceProviderExtensions
public static class ServiceProviderExtensions
{
public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
{
foreach (var type in serviceTypes)
{
services.AddTransient(type);
}
return services;
}
}
I then amended the existing controller to take the DI type (note there is just the one ctor)
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ProductsApp.Controllers
{
public class ProductsController : ApiController
{
DefaultProduct _dp = null;
public ProductsController(DefaultProduct dp)
{
_dp = dp;
//
products.Add(dp);
}
List<Product> products = new List<Product>()
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public IHttpActionResult GetProduct(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
}
My Default Dependency Resolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
I recommend you use Autofac, there are anothers fwk like unity, ninject, the benchmarks autofac has excelent perfomance.
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
Here is the integration with MVC (and works with all class)
http://docs.autofac.org/en/latest/integration/mvc.html
The simplest way to implements Dependency Injection in ASP.NET MVC 5 is to use the tool developed by Microsoft itself, called Unity.
You can find many resources on the internet about it, and you can start by reading the official documentation available here: Developer's Guide to Dependency Injection Using Unity
Also, is Dependency injection limited to controllers only or can it work with any class?
It works with any class, in any project, as long as you register the Interface related to the Implementation (if you want to take profit of the IoC pattern), all you have to do then is to add the Interface instantiation in your constructor.
In this video a Microsoft MVP demos dependency injection in MVC5 with AutoFac. Very clear explanation on how to set it up:
Dependency Injection MVC5 Demo
Source code is available on GitHub
From here https://scottdorman.blog/2016/03/17/integrating-asp-net-core-dependency-injection-in-mvc-4/
this line saved me.
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
I recommend using Windsor, by installing the nuget package Castle Windsor MVC Bootstrapper, then you can create a service that implements IWindsorInstaller, something like this:
public class ServiceRegister : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container,
Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();
container.Register(
Component
.For<IServiceToRegister>()
.ImplementedBy<ServiceToRegister>().
DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
.LifestyleTransient());
}
}
Then inside your controller something like this:
public class MyController
{
IServiceToRegister _serviceToRegister;
public MyController (IServiceToRegister serviceToRegister)
{
_serviceToRegister = serviceToRegister;//Then you can use it inside your controller
}
}
And by default the library will handle sending the right service to your controller by calling the install() of ServiceRegister at start up because it implements IWindsorInstaller
Having started from this thread to figure out how to use Microsoft.Extensions.DependencyInjection in my ASP.NET MVC 5 project, and reading and trying and failing, I finally came up with a solution that I wanted to shamelessly offer to the rest of you.
I pieced together a gist from David Fowler, the example code from Scott Dorman, and added in a bit of my own spice to create library that allows you to simulate ASP.NET Core's Startup in ASP.NET MVC "Classic".
For more information, please take a look at the GitHub repository for Arex388.AspNet.Mvc.Startup. If you're interested you can also read through my blog post about it, here (if it doesn't load, refresh until it does, the server's been giving me troubles and I haven't had time to investigate...). Hope it helps someone!

Microsoft Unity Service is null when controller is hit

I'm looking at Microsoft Unity injection for a webapi we've based on the full stack code i found here;
Full Stack WebApi GitHub Repo
Now when I run the code and hit the patients controller in this case, the
[Dependency]
public PatientService PatientService { get; set; }
is null.
Now the patient service is the entity having the context injected into its constructor.
public class PatientService : BaseService
{
private readonly Func<IUCareDataContext> _contextFactory;
/// <summary>
/// Initializes a new instance of the <see cref="PatientService"/> class.
/// </summary>
/// <param name="contextFactory">The context factory.</param>
public PatientService(Func<IUCareDataContext> contextFactory)
{
this._contextFactory = contextFactory;
}
From looking at the Unity documentation and UnityConfig.cs I can see that the Register is completed, but no Resolve?
namespace CCS.Services.WebApi
{
using CCS.Data.UCare;
using Data;
using Microsoft.Practices.Unity;
using System.Web.Http;
using Unity.WebApi;
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IUCareDataContext, UCareDataContext>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
}
Unity [Dependency] Attribute MSDN
Is this implementation missing something or should the dependency injection just work automatically when the controller is instantiated?
As it was pointed out I could inject the service. But this would then mean I would have to register every service to unity, with all services ultimately having the same context interface being injected to it. I would have thought there is a way around this?

Ninject with WebApi still rendering "Make sure that the controller has a parameterless public constructor"

I have already researched a number of errors based on the message "Make sure that the controller has a parameterless public constructor" and those answers did not appear to help. (see bottom)
Building an ASP.NET WEB API 2 application using Ninject.
I want to have an applicationController which will be polled from an Angular service to allow sharing some information across sessions. To accomplish this I have built an applicationService designed to be run as a singleton so that each web api session will share the same service. I am using Ninject 3.2.0.0 and have installed (using NPM based on these instructions)
Ninject
Ninject.Web.Common
Ninject.Web.WebApi
Ninject.Web.WebApi.WebHost
Which, by design, created the NinjectWebCommon where I have added the (I believe) necessary bindings. During an initial request the applicationService is instantiated (as expected), but when the call is made to the GET method of the applicationController I get:
An error occurred when trying to create a controller of type
'ApplicationController'. Make sure that the controller has a
parameterless public constructor.
the stack trace reveals:
at System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)
The fact that it is calling DefaultHttpControllerActivator makes me think that the Ninject Bindings are not correct, but the the only way the applicationService could be instantiated would be due to ninject.
The bindings in NinjectWebCommon
private static void RegisterServices(IKernel kernel)
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();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ScfProvider>().ToSelf().InSingletonScope();
kernel.Bind<ApplicationService>().ToSelf().InSingletonScope().WithConstructorArgument(kernel.Get<ScfProvider>());
kernel.Bind<ApplicationController>().ToSelf().InRequestScope().WithConstructorArgument(kernel.Get<ApplicationService>());
}
}
the ApplicationService:
public sealed class ApplicationService
{
private readonly ICommand _scfUpdater;
private Timer _timer;
public ApplicationService(ScfProvider scfUpdater)
{
if (scfUpdater == null) throw new ArgumentNullException(nameof(scfUpdater));
_scfUpdater = scfUpdater;
_activeUsers = new Dictionary<string, string>();
int scfInterval = int.Parse(ConfigurationManager.AppSettings["SCFUpdateInterval"]);
_timer = new Timer(SCFTick, this, 0, scfInterval);
}
#if DEBUG
public ICommand SCFUpdater => _scfUpdater;
#endif
private void SCFTick(object state)
{
_scfUpdater.Execute();
}
private readonly Dictionary<string, string> _activeUsers;
public Dictionary<string, string> ActiveUsers => _activeUsers;
public void UpdateUser(string userName, string status)
{
if (_activeUsers.ContainsKey(userName))
{
_activeUsers[userName] = status;
}
else
{
_activeUsers.Add(userName, status);
}
}
}
and finally, the applicationController:
public class ApplicationController : ApiController
{
private readonly ApplicationService _applicationService;
public ApplicationController(ApplicationService applicationService)
{
_applicationService = applicationService;
}
// GET: api/application
public IHttpActionResult Get()
{
_applicationService.UpdateUser(User.Identity.Name, "online");
return Ok(_applicationService.ActiveUsers);
}
...
}
(note: once I have this working more complex functionality will be added...but I need this working first...)
Other research:
Parameterless constructor error with Ninject bindings in .NET Web Api 2.1
(I have the correct references and a public c'tor)
Ninject Web Api "Make sure that the controller has a parameterless public constructor." (& several similar)
(As I understand it, WebActivatorEx and NinjectWebCommon resolve this matter)
I have recently ran into the problem with the parameterless constructor.
Having wasted time googling for solutions with solutions that were ok locally but didn't work when deployed the step I took were these.
Uninstalled nugets for Ninject.*
double checked bin folder and deleted any Ninject*.dlls
Commented out all code in the Ninject.Web.Common.cs files and renamed it as I wanted to
keep service bindings etc.. so I could reinstate in the Ninject.Web.Common.cs that would be created after reinstating the Ninject* nuget packages.
I then followed this guide but using the lates Ninject* nuget versions.
https://nodogmablog.bryanhogan.net/2016/04/web-api-2-and-ninject-how-to-make-them-work-together/
do not forget to add this in the newly created Ninject.Web.Common
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
before return kernel; in the private static CreateKernel method.

Categories

Resources