ApiControllers, LightInject and service injection in packages/content apps
Hi folks, looking for some insight/direction around how to set up my controllers in a V8 package/content app.
In V7, I'd typically do something like this:
public class MyApiController : UmbracoAuthorizedApiController
{
private readonly IFirstService _firstService;
private readonly IAnotherService _anotherService;
public MyApiController() : this(new FirstService(), new AnotherService())
{
}
public MyApiController(IFirstService firstService, IAnotherService anotherService)
{
_firstService = firstService;
_anotherService = anotherService;
}
// API methods
}
That will work fine without any additional setup - it's pretty standard DI stuff, makes it easy to replace the services with mocks/other implementations. Cool.
In V8 however, that same pattern throws a generic error - API controller does not have a parameterless controller. The stack trace references LightInject, so I'm assuming it's related to the new container?
I can work around it by newing up the services in the respective API methods, but that's shitty.
So... LightInject uses the constructor with the most parameters that it can resolve. If no constructor can fit, then you get the generic "Make sure that the controller has a parameterless public constructor" exception - but with an inner exception that should tell you why LightInject is not happy.
(reason why I'm asking for the stack trace)
In your example above, you have a parameterless constructor, so it should work. But maybe you got the error with a slightly different class. And then, typical DI as you said: you need to register your services in the container. Have you done this?
Would happen in a component (inheriting UmbracoComponentBase) Compose method.
Give me a bit more details and I'll be happy to help
It'd be really awesome to have a write-up on how to register services and implementations for plugins/content apps/whole sites, so we can start injecting stuff into both RenderMvcControllers and ApiControllers. Any chance you have something that could give us a head start?
public class UmbracoStartup : UmbracoComponentBase, IUmbracoUserComponent
{
public override void Compose(Composition composition)
{
composition.Container.RegisterSingleton<ISomethingService, SomethingService>();
composition.Container.Register<ISomethingPlugin, SomethingPlugin>();
}
public void Initialize()
{
// here we can register listeners etc, or do any other startup tasks
ServerVariablesParser.Parsing += ServerVariablesParser_Parsing;
}
}
Once those services are registered in Compose, they can be injected into controllers
public class MyController() : RenderMvcController {
private readonly ISomethingService _somethingService;
public MyController() : this(new SomethingService()) {}
public MyController(ISomethingService somethingService) {
_somethingService = somethingService;
}
}
It's the only way I could get it working, but I agree, it kinda defeats the purpose...
Tried it again just now without the default constructor, but LightInject complains about not having a parameterless public constructor. Highly likely it's user error on my part.
I don't think it's a user error on your part... you're seeing the same as I was when I played around with it.
Based on the previous comments I did manage to find the UmbracoComponentBase and the container registration stuff in its Compose method. But registering a service implementation didn't really work for my RenderMvcController - I got the exact same exception.
I also tried registering my service in a custom UmbracoApplication (I'm setting up a site so I have that luxury) but the same happens. I have yet to test if the registration works in an API controller.
It should (I believe) work in an ApiController - Umbraco's services are exposed on ServiceContext, ideally any other registered services would be there too.
Latest nightly handles this a bit differently - below is how I have it working, with proper DI in classes
public class MyComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Register<IMyThing, MyThing>();
composition.Components().Append<MyComponent>();
}
}
public class MyComponent : IComponent {
public void Initialize() {
// do stuff on startup
}
public void Terminate() {
// HASTA LA VISTA!
}
}
public class MyClass {
private readonly IMyThing _myThing;
public MyClass(IMyThing myThing) {
_myThing = myThing;
}
}
And to build on that, with a bit of a detour from what this thread was discussing, here's the next issue I've hit - more a general DI thing than Umbraco specific.
Trying to get property injection working - scenario is I have an interface, from which I derive a class. I want all instances of that class to have a service injected.
If I do it via ctor-injection, all derived classes must provide the parameter. I don't want that. Derived classes shouldn't need to know the parent implementation.
You want to inherit from a base class and not have to specify the injected parameter?
Personally, that's a no-no for me. Without constructor parameters you have no idea when each injected service in the base class is available in the lifetime of your derived objects. That can lead to hard to track bugs.
Do you need inheritance? Why are parameters a big deal. The derived classes need to know about the parent since that's how inheritance works.
I want the base class to provide data from the service, and have it available to the derived class, without the ctor injection
public class Foo : IFoo {
public IService Service { get; set; }
public int Digits => Service.GetDigits();
}
public class Bar : Foo {
public Bar() {
var x = Digits + 1;
}
}
If I have 20 different implementations of Foo, they shouldn't each need a reference to Service just to retrieve Digits.
If there's a better way to achieve this, I'm all for it.
In this case I recommend redesigning your classes to accept the service as a method parameter.
public class Foo {
public int Digits => Service.GetDigits(IService service);
}
public class Bar : Foo {
}
public class Caller
{
private readonly IService service;
public Caller(IService service) {
this.service = service;
}
public int GetDigits() {
Bar bar = new Bar();
return bar.Digits(this.Service) + 1;
}
}
public class MyComponent : IComponent {
public void Initialize() {
// do stuff on startup
}
public void Terminate() {
// HASTA LA VISTA!
}
}
Call me crazy but this seems more.... Well more.
IComponent : IDisposable
{
}
public class MyComponent : IComponent {
public MyComponent() {
// do stuff on startup
}
public void Dispose() {
// HASTA LA VISTA!
}
}
Ah well, guess what? That was my original design and it got changed by... popular demand. Not that you are not popular ;-) So yea... I'm not sure, don't know if there is a "right" answer to that one.
As for the rest of the thread... I'm following it and plan to post a reply.
I'm trying to follow this pattern to do some initialization but I am falling at the first hurdle - the class UmbracoComponentBase doesn't exist. Even the namespace
UmbracoComponentBase never made it to 8.0.0 indeed. The more recent comments on this post do things differently. You may also want to read this post for more details.
ApiControllers, LightInject and service injection in packages/content apps
Hi folks, looking for some insight/direction around how to set up my controllers in a V8 package/content app.
In V7, I'd typically do something like this:
That will work fine without any additional setup - it's pretty standard DI stuff, makes it easy to replace the services with mocks/other implementations. Cool.
In V8 however, that same pattern throws a generic error - API controller does not have a parameterless controller. The stack trace references LightInject, so I'm assuming it's related to the new container?
I can work around it by newing up the services in the respective API methods, but that's shitty.
How should this be managed in V8?
just to be sure can you share the full stack trace?
will reply with full details asap
So... LightInject uses the constructor with the most parameters that it can resolve. If no constructor can fit, then you get the generic "Make sure that the controller has a parameterless public constructor" exception - but with an inner exception that should tell you why LightInject is not happy.
(reason why I'm asking for the stack trace)
In your example above, you have a parameterless constructor, so it should work. But maybe you got the error with a slightly different class. And then, typical DI as you said: you need to register your services in the container. Have you done this?
Would happen in a component (inheriting
UmbracoComponentBase
)Compose
method.Give me a bit more details and I'll be happy to help
Thanks Stephen - I'd missed the
Compose
method. Added that in to my startup class, registered the services and everything is working fine.Knew it wouldn't be anything major.
This change means (correct me if I'm wrong) that any backoffice extension must use LightInject for DI?
At the moment: yes. Soon: no. What the
Compose
method will get is a LightInject-agnostic register. No dependency on LightInject. Stay tuned.It'd be really awesome to have a write-up on how to register services and implementations for plugins/content apps/whole sites, so we can start injecting stuff into both RenderMvcControllers and ApiControllers. Any chance you have something that could give us a head start?
FWIW, here's my current implementation:
Once those services are registered in
Compose
, they can be injected into controllersAt least, that's my understanding.
Thanks Nathan. I'll give that a spin soon as I can.
Do you need the default constructor? It doesn't look terribly decoupled having to pass default implementations of services to constructors.
It's the only way I could get it working, but I agree, it kinda defeats the purpose...
Tried it again just now without the default constructor, but LightInject complains about not having a parameterless public constructor. Highly likely it's user error on my part.
Thanks for getting back.
I don't think it's a user error on your part... you're seeing the same as I was when I played around with it.
Based on the previous comments I did manage to find the
UmbracoComponentBase
and the container registration stuff in itsCompose
method. But registering a service implementation didn't really work for myRenderMvcController
- I got the exact same exception.I also tried registering my service in a custom
UmbracoApplication
(I'm setting up a site so I have that luxury) but the same happens. I have yet to test if the registration works in an API controller.It should (I believe) work in an ApiController - Umbraco's services are exposed on ServiceContext, ideally any other registered services would be there too.
Will try to find time today to fiddle more.
Latest nightly handles this a bit differently - below is how I have it working, with proper DI in classes
Thanks Nathan, works like a charm now!
And to build on that, with a bit of a detour from what this thread was discussing, here's the next issue I've hit - more a general DI thing than Umbraco specific.
Trying to get property injection working - scenario is I have an interface, from which I derive a class. I want all instances of that class to have a service injected.
If I do it via ctor-injection, all derived classes must provide the parameter. I don't want that. Derived classes shouldn't need to know the parent implementation.
You don't want property injection. That introduces temporal coupling which is a Bad Thing ™
So how do I get out of this dilly of a pickle in which I am mired?
Just to clarify.
You want to inherit from a base class and not have to specify the injected parameter?
Personally, that's a no-no for me. Without constructor parameters you have no idea when each injected service in the base class is available in the lifetime of your derived objects. That can lead to hard to track bugs.
Do you need inheritance? Why are parameters a big deal. The derived classes need to know about the parent since that's how inheritance works.
I want the base class to provide data from the service, and have it available to the derived class, without the ctor injection
If I have 20 different implementations of Foo, they shouldn't each need a reference to Service just to retrieve Digits.
If there's a better way to achieve this, I'm all for it.
In this case I recommend redesigning your classes to accept the service as a method parameter.
Call me crazy but this seems more.... Well more.
Ah well, guess what? That was my original design and it got changed by... popular demand. Not that you are not popular ;-) So yea... I'm not sure, don't know if there is a "right" answer to that one.
As for the rest of the thread... I'm following it and plan to post a reply.
Should have stuck with your guns there chum, you definitely had it right first time. :)
Relying on an external caller to invoke an initialise call is more temporal coupling.
I'm trying to follow this pattern to do some initialization but I am falling at the first hurdle - the class UmbracoComponentBase doesn't exist. Even the namespace
I've got the following NuGet packages:
Umbraco.ModelsBuilder 8.0.1 Umbraco.ModelsBuilder.Ui 8.0.1 Umbraco.SqlServerCE 4.0.0.1 UmbracoCms 8.0.0 UmbracoCms.Core 8.0.0 UmbracoCms.Web 8.0.0
Where did you guys get this class from?
UmbracoComponentBase
never made it to 8.0.0 indeed. The more recent comments on this post do things differently. You may also want to read this post for more details.Feel free to have a poke around in github.com/nathanwoulfe/plumber8
The .Web and .Core projects both use the composer pattern, but only .Core uses both composer and component.
is working on a reply...