Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception on IHostApplicationLifetime.StopApplication() #49473

Closed
Leon99 opened this issue Mar 11, 2021 · 9 comments
Closed

Exception on IHostApplicationLifetime.StopApplication() #49473

Leon99 opened this issue Mar 11, 2021 · 9 comments
Labels
area-Extensions-Hosting untriaged New issue has not been triaged by the area owner

Comments

@Leon99
Copy link

Leon99 commented Mar 11, 2021

xxx type only implements IAsyncDisposable. Use DisposeAsync to dispose the container. is thrown on calling IHostApplicationLifetime.StopApplication() if there's a service in the container that implements IAsyncDisposable but not IDisposable. Any ideas how to deal with it (other than calling DisposeAsync().GetAwaiter().GetResult(); from Dispose(), which may lead to a deadlock)? I suspect having IHostApplicationLifetime.StopApplicationAsync() could help, but it doesn't exist.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Mar 11, 2021
@ghost
Copy link

ghost commented Mar 11, 2021

Tagging subscribers to this area: @eerhardt, @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

xxx type only implements IAsyncDisposable. Use DisposeAsync to dispose the container. is thrown on calling IHostApplicationLifetime.StopApplication() if there's a service in the container that implements IAsyncDisposable but not IDisposable. Any ideas how to deal with it (other than calling DisposeAsync().GetAwaiter().GetResult(); from Dispose(), of course)? I suspect having IHostApplicationLifetime.StopApplicationAsync() could help, but it doesn't exist.

Author: Leon99
Assignees: -
Labels:

area-Extensions-Hosting, untriaged

Milestone: -

@ghost ghost added this to Untriaged in ML, Extensions, Globalization, etc, POD. Mar 11, 2021
@davidfowl
Copy link
Member

Can you show the stack trace? Where are disposing the host from?

@Leon99
Copy link
Author

Leon99 commented Mar 14, 2021

This exception was originally thrown at this call stack:
    Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.Dispose()
    Cli.CrawlService.StartAsync(System.Threading.CancellationToken) in CrawlService.cs
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
    Microsoft.Extensions.Hosting.Internal.Host.StartAsync(System.Threading.CancellationToken)
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
    System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
    Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(Microsoft.Extensions.Hosting.IHost, System.Threading.CancellationToken)
    ...
    [Call Stack Truncated]

@davidfowl
Copy link
Member

In the CrawlService, are you making a custom service scope and calling Dispose instead of DisposeAsync? Why?

@Leon99
Copy link
Author

Leon99 commented Mar 14, 2021

Hmm, looking at that stack - it's not exactly what I saw initially, I changed the code significantly since then but didn't save the stack trace (shame on me) and now I just tried to reproduce it. Initially, it was failing on StopApplication(), although it's on the last line in that method... Anyway, to answer your question - I don't call Dispose explicitly, but I do using var scope = _serviceScopeFactory.CreateScope(); and then resolve the service from that scope.

@davidfowl
Copy link
Member

davidfowl commented Mar 14, 2021

Unfortunately, the workaround for this is a big ugly:

public static class ServiceScopeExtensions
{
    public static AsyncServiceScope CreateAsyncServiceScope(this IServiceProvider serviceProvider)
    {
        return new AsyncServiceScope(serviceProvider.CreateScope());
    }
}

public struct AsyncServiceScope : IServiceScope, IAsyncDisposable
{
    private readonly IServiceScope _serviceScope;

    public AsyncServiceScope(IServiceScope serviceScope)
    {
        _serviceScope = serviceScope;
    }

    public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider;

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

    public ValueTask DisposeAsync()
    {
        if (_serviceScope is IAsyncDisposable ad)
        {
            return ad.DisposeAsync();
        }
        _serviceScope.Dispose();
        return ValueTask.CompletedTask;
    }
}

Then you can change your code to do this:

await using var scope = _serviceProvider.CreateAsyncServiceScope();

@davidfowl
Copy link
Member

See #43970 for more information.

@davidfowl
Copy link
Member

Closing this one

ML, Extensions, Globalization, etc, POD. automation moved this from Untriaged to Done Mar 17, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Apr 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-Hosting untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

3 participants