LightInject v6.0.0 Release Notes

Release Date: 2019-08-28 // over 4 years ago
  • ๐Ÿ”„ Change Log

    ๐Ÿ‘€ v6.0.0 (8/28/2019)

    ๐Ÿ‘€ Full Changelog

    ๐Ÿ”€ Merged Pull Requests

    โšก๏ธ Updated an xmldoc typo (7/12/2019) #491 (Hammerstad)

    โšก๏ธ Updated a typo in the xmldoc for ContainerOptions.LogFactory
    โšก๏ธ Update version (8/2/2019) #493 (danielmarbach)

    ๐Ÿ‘€ Given https://github.com/seesharper/LightInject/blob/master/src/LightInject/LightInject.csproj#L5 should the version also be reflected in the source file?
    โž• Added Scope Performance tests (8/11/2019) #498 (seesharper)

    ๐Ÿ‘€ Flow the scope when possible (8/28/2019) #500 (seesharper)

    ๐Ÿ‘ This PR adds support for "flowing" the scope whenever possible.

    This means that we pass the scope that requested a service throughout the object graph.

    Example

    container.RegisterScoped\<IFoo\>(factory =\> new Foo());
    

    The factory passed into the factory delegate is an IServiceFactory and this used to always be the ServiceContainer implementing the IServiceFactory interface. What is changed is that we now pass in the Scope which also implements IServiceFactory.

    When LightInject was born, there was always this notion of a current scope. An ambient context that can be used to access the current scope.
    ๐Ÿ‘ It supports scenarios like

    using (container.BeginScope()) { var foo = container.GetInstance\<IFoo\>(); }
    

    The way this works is that when IFoo is requested we ask for the current scope and use that scope for resolving the instance.

    ๐Ÿ‘ Then we introduced support for getting a service directly from a scope.

    using (var outerScope = container.BeginScope()) { using(var innerScope = container.BeginScope()) { var outerFoo = outerScope.GetInstance\<IFoo\>(); var innerFoo = innerScope.GetInstance\<IFoo\>(); } }
    

    โช The way that this used to work was that we swapped the current scope with the scope from which the service was requested. After we resolved the service we restored the current scope to the previous current scope.

    This was problematic in a number of ways.

    • Swapping the current scope performs really bad as most of the time the current scope is backed by an AsyncLocal<T>.
    • Deferred resolution such as Func<T> and Lazy<T> that always relied on the current scope could end up using the "wrong" scope if someone or something started a new scope using container.BeginScope which in turn sets the current scope to new newly created scope.
    • Multiple parallel scopes was almost impossible to get right because of the assumption that we always have the correct current scope.

    Instead of always relying on the current scope, we pass the scope throughout the code that is generated to resolve an instance. One important aspect of this is that we do this with as little breakage as possible. That means that of the service is directly requested from the scope, we pass that scope without messing with the current scope. If we request a service from the container, the current scope will be used as before.

    ๐ŸŽ It should be noted that requesting service directly from the scope is the preferred way with regards to performance and correctness. We will not remove support for the ambient scope, but we might emit a warning log message in the future.

    Closed Issues

    • ๐Ÿ‘€ Decorator patter with a lifetime that doesn't match the default (5/23/2019) #478 (bounav)
    • ASP.NET Core SetCompatibilityVersion(CompatibilityVersion.Version_2_2) Breaks LightInject (5/23/2019) #482 (nystrup)
    • ๐Ÿ‘€ AspNetCore 2.2 AspNetCoreHostingModel InProcess not working (5/22/2019) #488 (valeriob)
    • ๐Ÿ‘€ Issue w/ Lazy<> and Scoped unit tests. (8/9/2019) #497 (danyhoron)