I have started a Web API project, and extended it to also be a normal MVC project. By this I mean I have some controllers deriving from ApiController and others from Controller.
Here's my DependencyResolver:
public class StructureMapDependencyResolver :IDependencyResolver
{
public IDependencyScope BeginScope()
{
return this;
}
public StructureMapDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType.IsAbstract || serviceType.IsInterface)
return _container.TryGetInstance(serviceType);
return _container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances<object>()
.Where(s => s.GetType() == serviceType);
}
private readonly IContainer _container;
public void Dispose() { }
}
and here's my Global.asax.cs:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ObjectFactory.Initialize(x =>
{
x.For<IDataService>().Use<MockDataService>();
});
ObjectFactory.Configure(x => x.Scan(scan =>
{
scan.AssembliesFromApplicationBaseDirectory();
scan.LookForRegistries();
scan.TheCallingAssembly();
scan.WithDefaultConventions();
}));
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.DependencyResolver =
new StructureMapDependencyResolver(container);
}
The Web API controllers are working great, but I can't get constructor injection to work in the Controller-derived ones. It's the usual No parameterless constructor defined for this object error message of course.
I'm at a bit of a loss here... this is my first foray into Web API so I'm not sure where I'm going wrong.
You need to call the DependencyResolver.SetResolver to your MVC IOC container. Make sure your container implements MVC's IDependencyResolver as well, otherwise it won't work for MVC.
Related
I have an aspnetcore server (serving a Blazor wasm app), which has a Quartz scheduled job running. When the job triggers, it is calling a method on one of my server's controllers.
If I call this method on my controller normally (e.g. via a web API call), it works fine.
When I call the method from the Quartz IJob, the DbContext used in the controller seems to be disposed.
I've tried injecting the controller into the job in the normal way, and also via IServiceProvider, and both have the same result.
Controller:
public class NotificationController : ControllerBase
{
private readonly ApplicationDbContext context;
public NotificationService(ApplicationDbContext context)
{
this.context = context;
}
public async Task MyMethod()
{
await context.SaveChangesAsync(); //This is where it fails when Quartz calls it, seems context is not populated
}
}
My job (IServiceProvider attempt):
public class ReminderJob : IJob
{
private readonly IServiceProvider serviceProvider;
public ReminderJob(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public async Task Execute(IJobExecutionContext jobcontext)
{
using (var scope = serviceProvider.CreateScope())
{
await scope.ServiceProvider.GetRequiredService<NotificationController>().MyMethod();
}
}
}
My job (DI attempt):
public class ReminderJob : IJob
{
private readonly NotificationController notificationController;
public ReminderJob(NotificationController notificationController)
{
this.notificationController = notificationController;
}
public async Task Execute(IJobExecutionContext jobcontext)
{
await notificationController.MyMethod();
}
}
My Startup.cs (relevant lines in ConfigureServices):
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddControllersWithViews();
services.AddMvcCore().AddControllersAsServices();
services.AddRazorPages();
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionScopedJobFactory(); //I also tried passing options.CreateScope to this method, but no difference
q.AddJobAndTrigger<ReminderJob>(configuration);
});
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
}
No exception is thrown in VS when it attempts to do context.SaveChangesAsync(), however, a breakpoint directly after it is not hit, however when I check the details of context while debugging, it doesn't seem to be populated correctly.
How do I use the Controller from within the IJob, and ensure the Controller's dependencies are not disposed of?
How do I use the Controller from within the IJob
Do not use the controller in the Job.
Move/Extract/Refactor the desired functionality into a service abstraction
//Service abstraction
public interface INotificationService {
Task MyMethod();
}
public class NotificationService : INotificationService {
private readonly ApplicationDbContext context;
public NotificationService(ApplicationDbContext context) {
this.context = context;
}
public async Task MyMethod() {
await context.SaveChangesAsync();
}
}
and have the job and controller depend on the service to invoke the desired functionality.
public class NotificationController : ControllerBase {
private readonly INotificationService service;
public NotificationController (INotificationService service ) {
this.service = service ;
}
public async Task<IActionResult> MyMethod() {
await service.MyMethod();
return Ok();
}
}
public class ReminderJob : IJob {
private readonly INotificationService service;
public ReminderJob(INotificationService service) {
this.service = service;
}
public Task Execute(IJobExecutionContext jobcontext) {
return service.MyMethod();
}
}
And of course register all the necessary services with the DI container.
//...
services.AddScoped<INotificationService, NotificationService>();
//...
I wrote a custom IServiceProviderFactory and installed it in Program.cs of a new app like this:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new PropertyInjectingContainerFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
It does lead to the correct configure function in Startup.cs getting called:
public void ConfigureContainer(PropertyInjectingContainerFactory.Builder builder)
{
builder.AddInjectAttribute<InjectDependencyAttribute>();
}
However, my created container only ever resolves two services: IConfiguration and IHost.
Everything else is resolved by the default container apparantly (for instance a service like ILogger<T> on a controller). What do I do wrong?
Here's the code for my custom factory - and please understand that I probably should be using an existing third-party container, but I also want to understand how this all fits together.
public class PropertyInjectingContainerFactory : IServiceProviderFactory<PropertyInjectingContainerFactory.Builder>
{
public Builder CreateBuilder(IServiceCollection services)
{
return new Builder(services);
}
public IServiceProvider CreateServiceProvider(Builder containerBuilder)
{
return containerBuilder.CreateServiceProvider();
}
public class Builder
{
internal readonly IServiceCollection services;
internal List<Type> attributeTypes = new List<Type>();
public Builder(IServiceCollection services)
{
this.services = services;
}
public Builder AddInjectAttribute<A>()
where A : Attribute
{
attributeTypes.Add(typeof(A));
return this;
}
public IServiceProvider CreateServiceProvider()
=> new PropertyInjectingServiceProvider(services.BuildServiceProvider(), attributeTypes.ToArray());
}
class PropertyInjectingServiceProvider : IServiceProvider
{
private readonly IServiceProvider services;
private readonly Type[] injectAttributes;
public PropertyInjectingServiceProvider(IServiceProvider services, Type[] injectAttributes)
{
this.services = services;
this.injectAttributes = injectAttributes;
}
// This function is only called for `IConfiguration` and `IHost` - why?
public object GetService(Type serviceType)
{
var service = services.GetService(serviceType);
InjectProperties(service);
return service;
}
private void InjectProperties(Object target)
{
var type = target.GetType();
var candidateProperties = type.GetProperties(System.Reflection.BindingFlags.Public);
var props = from p in candidateProperties
where injectAttributes.Any(a => p.GetCustomAttributes(a, true).Any())
select p;
foreach (var prop in props)
{
prop.SetValue(target, services.GetService(prop.PropertyType));
}
}
}
}
Exception:
Nullreference exception
Here is my Code:
Controller:
private IUserService UserService;
public HomeController()
{
}
public HomeController(IUserService UserService)
{
this.UserService = UserService;
}
[HttpPost]
public ActionResult Register(RegisterViewModel reg)
{
SADM_Users users = new SADM_Users();
if (reg == null)
{
return Json(false);
}
else
{
FillUserMaster(users, reg);
UserService.insertUser(users);
ViewBag.ErrorMsg = "Succesfully added";
}
return View();
}
Service Layer:
public interface IUserService
{
void insertUser(SADM_Users users);
}
public class UserService:IUserService
{
private readonly ILoginRepository LoginRepository;
private readonly IUnitOfWork unitOfWork;
public UserService(ILoginRepository LoginRepository)
{
this.LoginRepository = LoginRepository;
}
public void insertUser(SADM_Users users)
{
try
{
LoginRepository.Add(users);
unitOfWork.Commit();
}
finally {
users = null;
}
}
}
I'm creating an mvc app with a service layer. I'm used to call services using in my controllers, but these services have not been Called.
please some one help on this.and i wnt to know any dependancy factor.
Remove the default constructor from your HomeController and make sure that you are using a DI library that will inject the proper implementation of IUserService into it.
I want to inject unity container into WebController.
I have UnityDependencyResolver:
public class UnityDependencyResolver : IDependencyResolver
{
readonly IUnityContainer _container;
public UnityDependencyResolver(IUnityContainer container)
{
this._container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch
{
return new List<object>();
}
}
public void Dispose()
{
_container.Dispose();
}
}
Then, in my Global.asax I add the following line:
var container = new UnityContainer();
container.RegisterType<IService, Service>
(new PerThreadLifetimeManager()).RegisterType<IDALContext, DALContext>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Then, If I use the following in a Web Controller:
private IService _service;
public HomeController(IService srv)
{
_service = srv;
}
It works fine.
But I want to inject it into WebAPI Controller, so if I do it the same way:
private IService _service;
public ValuesController(IService srv)
{
_service = srv;
}
It does not work, it says that constructor is not defined.
Ok, I create one more constructor:
public ValuesController(){}
And in this case it uses only this constructor and never the one where I should inject unity container.
Please advise.
Add this in your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Routes and other stuff here...
var container = IocContainer.Instance; // Or any other way to fetch your container.
config.DependencyResolver = new UnityDependencyResolver(container);
}
}
And if you want the same container you can keep it in a static variable, like so:
public static class IocContainer
{
private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
return container;
});
public static IUnityContainer Instance
{
get { return Container.Value; }
}
}
More info can be found here:
http://www.asp.net/web-api/overview/advanced/dependency-injection
On a sidenote, I can also recommend the nuget-package Unity.Mvc. It adds a UnityWebActivator and support for PerRequestLifetimeManager.
https://www.nuget.org/packages/Unity.Mvc/
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
MVC3 + Ninject - How to?
In a small mvc 4 project, I'm trying to implement dependency injection using ninject.
So far, I have it working with api controllers, but I'm not having any luck with regular controllers.
I have A NinjectResolver:
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(_kernel.BeginBlock());
}
public override void Dispose()
{
_kernel.Dispose();
}
}
And a NinjectScope:
public class NinjectDependencyScope : IDependencyScope
{
protected IResolutionRoot ResolutionRoot;
public NinjectDependencyScope(IResolutionRoot kernel)
{
ResolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
var request = ResolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return ResolutionRoot.Resolve(request).SingleOrDefault();
}
public IEnumerable<object> GetServices(Type serviceType)
{
var request = ResolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return ResolutionRoot.Resolve(request).ToList();
}
public void Dispose()
{
var disposable = (IDisposable)ResolutionRoot;
if (disposable != null) disposable.Dispose();
ResolutionRoot = null;
}
}
Then I set it up in my Global.asax:
var kernel = new StandardKernel();
kernel.Bind(typeof(IRepository)).To(typeof(Repository));
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
Then I inject a repository into an api controller like this:
private IRepository _repository;
public TestApiController(IRepository repository)
{
_repository = repository;
}
This works fine, but doing the same in a regular controller fails with the
"No parameterless constructor defined for this object" error.
Any ideas?
ASP.NET MVC uses a different IDependencyResolver than ASP.NET WebAPi namelly Sytem.Web.Mvc.IDependencyResolver.
So you need a new NinjectDependencyResolver implementation for the MVC Controllers (luckily the MVC and WepAPi IDependencyResolver almost has the same members with the same signatures, so it's easy to implement)
public class NinjectMvcDependencyResolver : NinjectDependencyScope,
System.Web.Mvc.IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
_kernel = kernel;
}
}
And then you can register it in the Global.asax with:
DependencyResolver.SetResolver(new NinjectMvcDependencyResolver(kernel));