I can't register dependencies by convention.
container.RegisterAssembly() is completely unusable as the AssemblyScanner has been replaced with a NOP stub.
Line 61
Line 254
I am seeing this issue on Umbraco version: 8.5.2
Spin up umbraco 8.5.2 site
Add the following to a c# file
public class Composer : IUserComposer
{
public void Compose(Composition composition)
{
var container = composition.Concrete as ServiceContainer;
Debug.Assert(container != null, nameof(container) + " != null");
container.RegisterAssembly(typeof(IFoo).Assembly);
// The following throws
container.GetInstance<IFoo>();
}
}
public interface IFoo
{
string Ping();
}
public class Foo : IFoo
{
public string Ping()
{
return "Pong";
}
}
I expect to be able to resolve an instance implementing IFoo
It doesn't work.
However if I blast over the NOP scanner with the following it works as one would expect.
public void Compose(Composition composition)
{
var container = composition.Concrete as ServiceContainer;
Debug.Assert(container != null, nameof(container) + " != null");
// Replace NOP scanner
container.AssemblyScanner = new ServiceContainer().AssemblyScanner;
container.RegisterAssembly(typeof(IFoo).Assembly);
// The following does not throw
container.GetInstance<IFoo>();
}
As far as I can tell, we disable registering assemblies on purpose because it's a very expensive operation, which significantly slows down the startup time of Umbraco.
Without knowing too much about what you're trying to do, I am going to assume that you need to register certain types from your assembly?
This documentation could be of use to you, implementing IDiscoverable on the types you need would be the way to go: https://our.umbraco.com/documentation/implementation/composing/#example-of-using-lazy-collections-with-type-scanning
However, registering types explicitly is preferred for performance reasons, as noted in this documentation as well.
Does that help?
So disable it for yourselves not for everyone, I want to register types in my assemblies (e.g my application services) by convention.
The docs even suggest that this should be possible
https://our.umbraco.com/documentation/reference/using-ioc/ #Accessing LightInject container.
Sorry to make you angry 馃槃
I am not familiar enough with the subject to know why we list it in the documentation, but I will ask around! 馃憤
I'm not angry, just trying to be helpful, something is wrong, I want to raise awareness of that.
Will even try to fix it and submit a PR.
Thanks! First let me ask around if there is actually a problem, I wouldn't want you to put in the work if we're not going to be able to use it. I'll get back to you.
The problem is that if you are going to advertise that I can hook into the underlying IOC container to make use of its API instead of the thin wrapper provided by Umbraco you shouldn't be altering the setup of the underlying container thereby making the LightInject documentation irrelevant.
Preventing LightIntject from scanning all of the umbraco assemblies should be possible without breaking the functionality for everyone else.
Hi @rustybox !
Many thanks for this info and your work in the PR. I had to look into a bunch of this myself to see what is going on and I think you probably found some of the same things as I did. As you've noted assembly scanning does occur when GetEmitMethod which I think is the reason why assembly scanning was disabled because we don't really want assemblies scanned when this occurs. I think ideally we just want assemblies scanned for using RegisterAssembly explicitly (which we don't use ourselves in the core). I assume you also found this LI issue https://github.com/seesharper/LightInject/issues/103
I've left a review on your PR in case we want to use all/some of that code and kudos on writing the unit tests.
With some additional investigation, it turns out that assembly scan is called primarily for these assemblies: mscorlib, System.Web.Mvc, System.Web.Http (and friends), all compiled razor view assemblies and it occurs on startup and pretty much anytime a controller is created which is a lot. When controllers are created then it starts trying to scan Umbraco.Web too.
It would be nice if there was an easier option in LI to just disable assembly scanning for GetEmitEvent (i.e. https://github.com/seesharper/LightInject/pull/343/files) but alas it seems we have to work around it and I think it's best assembly scanning is complete disabled for this for all assemblies. One way AFAIK is your way by introducing a RegisterFallback method which requires some additional checks, the other is to only allow assembly scanning for RegisterAssembly calls which I've done in this PR https://github.com/umbraco/Umbraco-CMS/pull/7765
Let me know what you think.
Cheers @Shazwazza, aye I had a good look through the related issues in LightInject.
As mentioned on your PR I have a wildcard 3rd way in the first commit of my PR which I refactored out that achieves the same outcome as your PR without swapping the scanner back and forth, it's less verbose but also less obvious without digging into the internals of LightInject.
I'm not sure what my preference would be, I think all of the options are bad. In an ideal world it would be much nicer to refactor to MS DI abstractions as discussed in U4-11427 and allow users to pick their own container e.g. Lamar, StructureMap, Autofac.
I can't say i'm particularly fond of LightInject for larger projects, although I could see it being very useful in certain projects especially in single file mode.
@rustybox That MS DI abstraction discussion is very very old, there's a few newer heated debates somewhere here on GH ;) The thing is that it is possible to change the underlying DI container, it just needs to conform to our abstractions (yes 'conforming container', many arguments, etc... but MS DI is the same).
@lars-erik knows much more about this than I do though and he's built containers that work with Umbraco that can replace Light Inject, i just don't know if these are public packages, or just proof of concepts? Lars any chance you can provide a bit of info here about that?
@Shazwazza @rustybox
I did some proof of concepts while contributing on the DI parts of Umbraco fall 2018.
Stuff might have changed, but the code can be looked at here:
https://github.com/lars-erik/our.umbraco.containers
Interesting, so I could use Autofac/Lamar and pull the IContainer out in a UserComposer?
That could do with some documentation which I think is currently lacking, will see if I can find some time to have a go :)
@rustybox did you have any luck finding documentation around this?
No, writing some docs for it will be a long term goal for me if someone else doesn't get around to it first.
However I don't believe there is too much to it.
https://our.umbraco.com/apidocs/v8/csharp/api/Umbraco.Core.Composing.RegisterFactory.html
To override the default LightInjectContainer, add an appSetting named 'Umbraco.Core.RegisterType' with a fully qualified type name to a class with a static method "Create" returning an IRegister.
Then you just need to write an adapter for your IOC container, see @lars-erik https://github.com/lars-erik/our.umbraco.containers repo for inspiration
K thanks
@rustybox @richieallen
If you go the way of building a custom container, I highly recommend running all of Umbraco's container related tests on it like I've done here:
https://github.com/lars-erik/our.umbraco.containers/blob/master/umbraco-tests-castle.ps1
Since most containers behave a bit different from others you might tear holes in the time space continuum if you don't.
For instance, some containers let the first registered component be "the one" you get when you GetInstance, while others might let the last one be the winner. Others again might just not let you register multiple components for the same interface at all. That's just one possible problem - there's a reason why MS.DI is so debated. :)
Anyway, good luck, and don't hesitate to ask (tho answers might be vague/unhelpful, this is hard!). There's a channel about V8 DI on the community slack as well.
Closing, v8 lost cause, #8653
Most helpful comment
Interesting, so I could use Autofac/Lamar and pull the IContainer out in a UserComposer?
That could do with some documentation which I think is currently lacking, will see if I can find some time to have a go :)