Skip to content
This repository has been archived by the owner on Dec 19, 2018. It is now read-only.

Allow external service providers (#550 and #1309) #1322

Closed
wants to merge 8 commits into from
Closed

Allow external service providers (#550 and #1309) #1322

wants to merge 8 commits into from

Conversation

ENikS
Copy link
Member

@ENikS ENikS commented Jan 30, 2018

This proposed solution allows replacement of application as well as framework service providers.
It should satisfy requirements in both #550 and #1309 and might eliminate need for #1060

@ENikS ENikS changed the title Another take on #550 and #1309 Allow external service providers (#550 and #1309) Jan 30, 2018
@@ -153,7 +153,7 @@ public IWebHost Build()

var hostingServices = BuildCommonServices(out var hostingStartupErrors);
var applicationServices = hostingServices.Clone();
var hostingServiceProvider = hostingServices.BuildServiceProvider();
var hostingServiceProvider = GetProviderFromFactory(hostingServices) ?? hostingServices.BuildServiceProvider();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At present configured factories are ignored and builder uses hardcoded hostingServices.BuildServiceProvider(). Instead it should check if proper factory is registered and if yes, use it to create provider.
Fall back on hardcoded call if nothing is registered.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I almost like this change. Lets instead build the service provider another time:

var hostingServiceProvider = hostingServices.BuildServiceProvider();
hostingServiceProvider = GetProviderFromFactory(hostingServiceProvider) ?? hostingServiceProvider;

IServiceProvider GetProviderFromFactory(IServiceProvider provider)
{
    var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();
    return factory?.CreateServiceProvider(factory.CreateBuilder(colection));
}

It's a bit more robust.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

.ConfigureTestServices(services => services.AddSingleton(new SimpleService { Message = "OverridesConfigureServices" }))
.ConfigureServices(s => {
s.AddSingleton<IServiceProviderFactory<ThirdPartyContainer>, ThirdPartyContainerServiceProviderFactory>();
s.AddSingleton<IServiceProvider>((a) => s.BuildServiceProvider()); // Provide outside provider instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't required.

Copy link
Member Author

@ENikS ENikS Jan 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not. But I thought I'd include a demonstration of using these either separately or combined.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah lets not

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It hasn't been removed yet it seems.

@@ -202,6 +202,14 @@ public IWebHost Build()
host.Dispose();
throw;
}

IServiceProvider GetProviderFromFactory(IServiceCollection colection)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spelling colection-> collection

}
}


Copy link
Member

@davidfowl davidfowl Jan 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Remove extra space new line.

Assert.Equal("ApplicationServicesEqual:True", response);
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Remove extra space new line.

@ENikS
Copy link
Member Author

ENikS commented Jan 31, 2018

What time zone are you in? Do you work in the middle of the night?


IServiceProvider GetProviderFromFactory(IServiceCollection collection)
{
var provider = collection.BuildServiceProvider();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm after thinking bit, we might need to dispose the temporary container we created If there's an IServiceProviderFactory<IServiceCollection> so this code gets a bit more complicated:

if (factory != null)
{
     using (provider)
     {
          return factory.CreateServiceProvider(factory.CreateBuilder(collection))
     }
}
return provider;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't want to do that. In case factory is resolved from a type and had any disposable dependencies this code will discard these but it might still be used in the provider.
The provider should die quietly as any other Gen0 variable during next partial collection.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any service provider we create should be disposed so that services get disposed. The provider may not be collected if there’s a singleton relying on dispose to unroot itself (we had an example recently where a timer was causing problems). The reasons it’s ok here is because we’re creating a whole new container anyways so disposing services after creation should be fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand

@@ -71,6 +71,34 @@ public class ThirdPartyContainerStartup
$"{ctx.RequestServices.GetRequiredService<SimpleService>().Message}, {ctx.RequestServices.GetRequiredService<TestService>().Message}"));
}

[Fact]
public async Task ExternalContainerInstanceCanBeUsedForEverything()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this verify the custom container is being used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you like it to be verified? Can you think of all your comments and do them all at once?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not that familiar with your testing framework, I used one of the tests as example and run it in debugger to verify. Feel free to change it any way you like.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, sorry, I have a bad habit of re-reviewing things where they are big changes. A good test would be to add a service in the ExternalContainer implementation and make sure it's resolved in the Startup constructor.

I would also not add a test to TestServerTests.cs I would add a test to https://github.com/aspnet/Hosting/blob/dev/test/Microsoft.AspNetCore.Hosting.Tests/WebHostBuilderTests.cs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this it? Think about it, I am catching flight in a few hours. I have time just for one more change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can take over if you want. It's pretty much done.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would appreciate if you do. I am getting low on time.

@davidfowl
Copy link
Member

Replaced by #1325

@davidfowl davidfowl closed this Jan 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants