LightInject icon indicating copy to clipboard operation
LightInject copied to clipboard

Using LightInject with ASP.Net vNext

Open eswann opened this issue 10 years ago • 16 comments

Hi Berhard, I was trying out some IOC containers with ASP.Net vNext, and thought I'd try to implement the built in DI using LightInject, as it's one of the DI containers I like. There's already an opensource project on Github hosted by M$ for this purpose. It has a few of the popular containers implemented and has a base set of tests that it runs on all containers. (https://github.com/aspnet/DependencyInjection)

I've created a fork and made an attempt to implement this with LightInject, (https://github.com/eswann/DependencyInjection) but I've got several failing tests to work out and I'm stuck on one failing test in particular. It's essentially a scope issue. I'm guessing there's a decent way to handle this. There is a test (ScopedServiceCanBeResolved) which basically goes like:

 var container = CreateContainer();
 var scopeFactory = container.GetService<IServiceScopeFactory>();
 using (var scope = scopeFactory.CreateScope())
 {
            //This is expected to get an "unscoped" instance
            var containerScopedService = container.GetService<IFakeScopedService>();
            //The following items are expected to get "scoped" instances
            var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>();
            Thread.Sleep(200);
            var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>();
            //This fails because we're really just accessing the same container inside of the scope so 
            //the same instance is returned
            Assert.NotEqual(containerScopedService, scopedService1);
            Assert.Equal(scopedService1, scopedService2);
 }

So a scope is created, and the test asks for an instance from the main container (which it expects to be unscoped) and it then it asks for an instance from the scope (which essentially amounts to a child container) and expects it to be different. But LightInject doesn't really have a child container concept that I can see. Once I start a scope, my original LightInject container also operates within that scope.

There are two main interfaces that have to be implemented: IServiceScope and IServiceScopeFactory. The scope is a common interface that I had to implement and I just for now have it just wrapping an LI IServiceFactory and creating/disposing a new scope. But obviously this doesn't meet the requirements of the DI tests. Whether or not this is important...I don't really know but passing all of the provided tests would probably be a good idea.

internal class LightInjectServiceScope : IServiceScope
{
    private readonly IServiceFactory _factory;
    private readonly Scope _scope;
    public LightInjectServiceScope(IServiceFactory factory)
    {
        _factory = factory;
        _scope = _factory.BeginScope();
    }

    public IServiceProvider ServiceProvider => new LightInjectServiceProvider(_factory);

    public void Dispose()
    {
        _scope.Dispose();
    }
}

Anyway...any ideas on how to accomplish this would be appreciated.

Thanks! Eric

eswann avatar Nov 18 '14 15:11 eswann

Hi Eric.

I understand that you live on the bleeding edge from a technological point of view ;)

When it comes to ASP.Net vNext, I have not looked into this yet as it seems to require Visual Studio 2015. At least in order to open the projects in this repository.

I could not help but notice that you have committed something similar for SimpleInjector. I can only assume that you ran into a similar problem there since LightInject and SimpleInjector are quite similar when in comes to scope handling. I also suspect that this DI abstraction can change before it goes RTM.

Do you know if it is possible to run any of this in Visual Studio 2013?

Anyway, I appreciate the effort and I will take a look at it as soon as I get VS2015 installed.

Best regards

Bernhard Richter

On Tue, Nov 18, 2014 at 4:51 PM, Swannee [email protected] wrote:

Hi Berhard, I was trying out some IOC containers with ASP.Net vNext, and thought I'd try to implement the built in DI using LightInject, as it's one of the DI containers I like. There's already an opensource project on Github hosted by M$ for this purpose. It has a few of the popular containers implemented and has a base set of tests that it runs on all containers. (https://github.com/aspnet/DependencyInjection)

I've created a fork and made an attempt to implement this with LightInject, (https://github.com/eswann/DependencyInjection) but I've got several failing tests to work out and I'm stuck on one failing test in particular. It's essentially a scope issue. I'm guessing there's a decent way to handle this. There is a test (ScopedServiceCanBeResolved) which basically goes like:

var container = CreateContainer(); var scopeFactory = container.GetService<IServiceScopeFactory>(); using (var scope = scopeFactory.CreateScope()) { //This is expected to get an "unscoped" instance var containerScopedService = container.GetService<IFakeScopedService>(); //The following items are expected to get "scoped" instances var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>(); Thread.Sleep(200); var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>(); //This fails because we're really just accessing the same container inside of the scope so //the same instance is returned Assert.NotEqual(containerScopedService, scopedService1); Assert.Equal(scopedService1, scopedService2); }

So a scope is created, and the test asks for an instance from the main container (which it expects to be unscoped) and it then it asks for an instance from the scope (which essentially amounts to a child container) and expects it to be different. But LightInject doesn't really have a child container concept that I can see. Once I start a scope, all my original LightInject container also operations within that scope.

There are two main interfaces that have to be implemented: IServiceScope and IServiceScopeFactory. The scope is a common interface that I had to implement and I just for now have it just wrapping an IServiceFactory and creating/disposing a new scope. But obviously this doesn't meet the requirements of the DI tests. Whether or not this is important...I don't really know but passing all of the provided tests would probably be a good idea.

internal class LightInjectServiceScope : IServiceScope { private readonly IServiceFactory _factory; private readonly Scope _scope; public LightInjectServiceScope(IServiceFactory factory) { _factory = factory; _scope = _factory.BeginScope(); }

public IServiceProvider ServiceProvider => new LightInjectServiceProvider(_factory);

public void Dispose()
{
    _scope.Dispose();
}

}

Anyway...any ideas on how to accomplish this would be appreciated.

Thanks! Eric

— Reply to this email directly or view it on GitHub https://github.com/seesharper/LightInject/issues/132.

seesharper avatar Nov 18 '14 20:11 seesharper

Hey Bernhard...yeah I was also playing with SimpleInjector as that is another DI container I like :). As you mentioned, I ran into some similar issues with SimpleInjector so thought I'd try my luck with LightInject. Both of them have similar issues in this vein, but frankly I feel that LightInject has a simpler approach to some things (in a good way) and that might make it easier.

The main reason I'm trying out VS 2015 is that I really like the "combined aspnet" and the unified DI concepts. In addition, I was also trying out their Cordova project time so decided to jump in feet first. There really is a lot of truly exciting stuff in this release. I do have to warn you that VS 2015 is still pretty buggy in the intellisense area and if you use Resharper as I do, it's also very buggy (but kinda works). The core stuff all seems really solid though.

Any help will be appreciated...I'll continue to work through the other failing tests and see where I get.

eswann avatar Nov 18 '14 23:11 eswann

Hey Bernhard...did you ever try this out with vNext? I hear it's going to drop ~mid-year...I basically ran into a wall with the primary issue I had here: There is a test (ScopedServiceCanBeResolved) which basically goes like:

 var container = CreateContainer();
 var scopeFactory = container.GetService<IServiceScopeFactory>();
 using (var scope = scopeFactory.CreateScope())
 {
        //This is expected to get an "unscoped" instance
        var containerScopedService = container.GetService<IFakeScopedService>();
        //The following items are expected to get "scoped" instances
        var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>();
        Thread.Sleep(200);
        var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>();
        //This fails because we're really just accessing the same container inside of the scope so 
        //the same instance is returned
        Assert.NotEqual(containerScopedService, scopedService1);
        Assert.Equal(scopedService1, scopedService2);
 }

So a scope is created, and the test asks for an instance from the main container (which it expects to be unscoped) and it then it asks for an instance from the scope (which essentially amounts to a child container) and expects it to be different. But LightInject doesn't really have a child container concept that I can see. Once I start a scope, my original LightInject container also operates within that scope.

eswann avatar Mar 06 '15 20:03 eswann

I might have to look into adding support for child containers in order to support this scenario.

seesharper avatar Apr 09 '15 08:04 seesharper

Hi @seesharper. I would also be very interested in child container support. I am wanting a fresh child container per test, so that I can override registrations in a test, and then have them disposed at the end of the test. Is that something I can do already with scopes, or would the new child container support be the only way to achieve that? Thanks.

mwhelan avatar May 03 '15 15:05 mwhelan

Now that ASP.NET 5 RC1 is out and production ready, what is the status of ASP.NET 5 support in LightInject? Strongly considering migrating to ASP.NET 5, would like to have my favourite DI library on board :)

geirsagberg avatar Nov 23 '15 15:11 geirsagberg

LightInject is already set up for dnxcore50 (CoreCLR) and dnx451. The missing piece is the actual DI adapter. MS is working on a test suite that is going to be available from Nuget. Once that is out, LightInject will provide the ASP.NET 5 DI abstraction. Maybe it's already published. I have not checked the last couple of days.

On Monday, 23 November 2015, Geir Sagberg [email protected] wrote:

Now that ASP.NET 5 RC1 is out and production ready, what is the status of ASP.NET 5 support in LightInject? Strongly considering migrating to ASP.NET 5, would like to have my favourite DI library on board :)

— Reply to this email directly or view it on GitHub https://github.com/seesharper/LightInject/issues/132#issuecomment-158968709 .

seesharper avatar Nov 23 '15 22:11 seesharper

Seems it was moved to the RC2 milestone: https://github.com/aspnet/DependencyInjection/issues/270, although it's marked as closed. No NuGet package found yet though. I'll see what I can do with eswann's fork in the meantime :)

geirsagberg avatar Nov 24 '15 08:11 geirsagberg

Nevermind, it is on the myget feed: https://www.myget.org/F/aspnetvnext/api/v2/package/Microsoft.Extensions.DependencyInjection.Specification.Tests/1.0.0-rc2-15807

geirsagberg avatar Nov 24 '15 08:11 geirsagberg

Do you know if there is one for RC1. They are still making breaking changes (renaming methods and so forth) in the dev branch for RC2. I could of course do an implementation based on RC2, but that would probably not even compile on RC1. As much as would love to get my hands dirty on this one I feel that this is still a moving target to say the least.

seesharper avatar Nov 24 '15 09:11 seesharper

The earliest build is 1.0.0-rc2-15788 it seems. I understand if you are reluctant to start implementing support until things have stabilized more; I believe the ASP.NET team mentioned a possible ETA for RC2 in January on a community standup, perhaps this issue might have to wait until then.

What kind of implementation changes to LightInject do you think will be needed for full support? I assume the child container issue is still relevant?

In any case I will give it a go on making the tests pass; will post here if I succeed.

geirsagberg avatar Nov 24 '15 11:11 geirsagberg

I don't really think any changes to the container are required. The child container behavior can easily be implemented using scopes. Or at least it seemed so the last time I checked. Regardless of the RC2 ETA, trying to write an adapter now would be a good exercise and it would probably require just minor changes to line up with the RC2/RTM version of ASP.NET 5. I appreciate the effort and don't hesitate to ask if you run into any issues.

seesharper avatar Nov 24 '15 12:11 seesharper

I got all tests passing now. Question is ..what should the Nuget package be called?

-- Microsoft.Extensions.DependencyInjection.LightInject ?

This might be good for searching Nuget

-- LightInject.AspNet? Not really tied to AspNet

LightInject.DependencyInjection?

seesharper avatar Jan 21 '16 15:01 seesharper

Decision made. LightInject.Microsoft.DependencyInjection it is.

https://github.com/seesharper/LightInject.Microsoft.DependencyInjection

seesharper avatar Jan 21 '16 19:01 seesharper

Good stuff! I agree on the package naming, keeps it under the LightInject namespace but makes it obvious that this is connected to the new Microsoft DI standard.

geirsagberg avatar Jan 23 '16 10:01 geirsagberg

Here is the first pre release version https://github.com/seesharper/LightInject.Microsoft.DependencyInjection

Also available from Nuget

seesharper avatar Jan 29 '16 15:01 seesharper