Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds support for compiling delegates up front.
Initial docs
Compilation
LightInject uses dynamic code compilation either in the form of System.Reflection.Emit or compiled expression trees. When a service is requested from the container, the code needed for creating the service instance is generated and compiled and a delegate for that code is stored for lookup later on so that we only compile it once. These delegates are stored in an AVL tree that ensures maximal performance when looking up a delegate for a given service type. If fact, looking up these delegates is what sets the top performing containers apart. Most high performance container emits approximately the same code, but the approach to storing these delegates may differ.
LightInject provides lock-free service lookup meaning that no locks are involved for getting a service instance after its initial generation and compilation. The only time LightInject actually creates a lock is when generating the code for a given service. That does however mean a potential lock contention problem when many concurrent requests asks for services for the first time.
LightInject deals with this potential problem by providing an API for compilation typically used when an application starts.
The following example shows how to compile all registered services.
One thing to be aware of is that not all services are backed by its own delegate.
Consider the following service:
Registered and resolved like this:
In this case we only create a delegate for resolving
Foo
since that is the only service that is directly requested from the container. The code for creating theBar
instance is embedded inside the code for creating theFoo
instance and hence there is only one delegate created.We call
Foo
a root service since it is directly requested from the container.In fact lets just have a look at the IL generated for creating the
Foo
instance.What happens here is that a new instance of
Bar
is created and pushed onto the stack and then we create theFoo
instance. This is the code that the delegate forFoo
points to.The reason for such a relatively detailed explanation is to illustrate that we don't always create a delegate for a given service and by simply doing a
container.Compile()
we might create a lot of delegates that is never actually executed. Probably no big deal as long as we don't have tens of thousands of services, but just something to be aware of.LightInject does not attempt to identify root services as that would be very difficult for various reasons.
We can instead use a predicate when compiling services up front.
Open Generics
LightInject cannot compile open generic services since the actual generic arguments are not known at "compile" time.
We can however specify the generic arguments like this:
LightInject will create a log entry every time a new delegate is created so that information can be used to identify root services that could be compiled up front. In addition to this, a log entry (warning) is also created when trying to compile an open generic service up front.