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

Enumerable dependencies resolving not consistent when open generic is used (area-Extensions-DependencyInjection) #57333

Open
skclusive opened this issue Aug 13, 2021 · 6 comments

Comments

@skclusive
Copy link

Description

Enumerable dependencies are not resolved consistently when open generic services are registered later.

Ex:

Setup

interface IProcessor<T> {}

record AnyProcessor<T> : IProcessor<T>;

record IntProcessor : IProcessor<int>;

Registration

var services = new ServiceCollection();
services.TryAddEnumerable(ServiceDescriptor.Scoped(typeof(IProcessor<int>), typeof(IntProcessor)));
// Fix: ordering open generic above concretes
services.TryAddEnumerable(ServiceDescriptor.Scoped(typeof(IProcessor<>), typeof(AnyProcessor<>)));

Resolving

var serviceProvider = services.BuildServiceProvider();

using var scope = serviceProvider.CreateAsyncScope();

// bug is reproducible only when below line enabled
var processor = scope.ServiceProvider.GetService<IProcessor<int>>();

var processors = scope.ServiceProvider.GetService<IEnumerable<IProcessor<int>>>() ?? Enumerable.Empty<IProcessor<int>>();

// prints "IntProcessor -- IntProcessor" instead of IntProcessor -- AnyProcessor`1 if line 17 commented.
Console.WriteLine(string.Join(" -- ", processors.Select(p => p.GetType().Name)));

To Reproduce

sample repo to reproduce the bug https://github.com/skclusive/EnumerableCallSiteBugConsole

Configuration

  • `dotnet --info
    .NET SDK (reflecting any global.json):
    Version: 6.0.100-preview.7.21379.14
    Commit: 22d70b47bc

Runtime Environment:
OS Name: Windows
OS Version: 10.0.22000
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.100-preview.7.21379.14\

Host (useful for support):
Version: 6.0.0-preview.7.21377.19
Commit: 91ba017

.NET SDKs installed:
5.0.301 [C:\Program Files\dotnet\sdk]
5.0.400 [C:\Program Files\dotnet\sdk]
6.0.100-preview.7.21379.14 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.0-preview.7.21378.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.0-preview.7.21377.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.0-preview.7.21378.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

  • Microsoft Visual Studio Community 2022 Preview (64-bit) Version 17.0.0 Preview 3.0

Regression?

Other information

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Aug 13, 2021
@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.

@skclusive skclusive changed the title Enumerable dependencies resolving not consistent when open generic is used Enumerable dependencies resolving not consistent when open generic is used (area-Extensions-DependencyInjection) Aug 13, 2021
@skclusive
Copy link
Author

belongs to area-Extensions-DependencyInjection

@ghost
Copy link

ghost commented Aug 13, 2021

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

Issue Details

Description

Enumerable dependencies are not resolved consistently when open generic services are registered later.

Ex:

Setup

interface IProcessor<T> {}

record AnyProcessor<T> : IProcessor<T>;

record IntProcessor : IProcessor<int>;

Registration

var services = new ServiceCollection();
services.TryAddEnumerable(ServiceDescriptor.Scoped(typeof(IProcessor<int>), typeof(IntProcessor)));
// Fix: ordering open generic above concretes
services.TryAddEnumerable(ServiceDescriptor.Scoped(typeof(IProcessor<>), typeof(AnyProcessor<>)));

Resolving

var serviceProvider = services.BuildServiceProvider();

using var scope = serviceProvider.CreateAsyncScope();

// bug is reproducible only when below line enabled
var processor = scope.ServiceProvider.GetService<IProcessor<int>>();

var processors = scope.ServiceProvider.GetService<IEnumerable<IProcessor<int>>>() ?? Enumerable.Empty<IProcessor<int>>();

// prints "IntProcessor -- IntProcessor" instead of IntProcessor -- AnyProcessor`1 if line 17 commented.
Console.WriteLine(string.Join(" -- ", processors.Select(p => p.GetType().Name)));

To Reproduce

sample repo to reproduce the bug https://github.com/skclusive/EnumerableCallSiteBugConsole

Configuration

  • `dotnet --info
    .NET SDK (reflecting any global.json):
    Version: 6.0.100-preview.7.21379.14
    Commit: 22d70b47bc

Runtime Environment:
OS Name: Windows
OS Version: 10.0.22000
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.100-preview.7.21379.14\

Host (useful for support):
Version: 6.0.0-preview.7.21377.19
Commit: 91ba017

.NET SDKs installed:
5.0.301 [C:\Program Files\dotnet\sdk]
5.0.400 [C:\Program Files\dotnet\sdk]
6.0.100-preview.7.21379.14 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.0-preview.7.21378.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.0-preview.7.21377.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.18 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.0-preview.7.21378.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

  • Microsoft Visual Studio Community 2022 Preview (64-bit) Version 17.0.0 Preview 3.0

Regression?

Other information

Author: skclusive
Assignees: -
Labels:

untriaged, area-Extensions-DependencyInjection

Milestone: -

@davidfowl
Copy link
Member

cc @maryamariyan @eerhardt We should look into this.

@maryamariyan maryamariyan removed the untriaged New issue has not been triaged by the area owner label Aug 16, 2021
@maryamariyan maryamariyan added this to the 6.0.0 milestone Aug 16, 2021
@maryamariyan
Copy link
Member

Moving to 7.0 since it's not a regression and we're closing down on 6.0

@maryamariyan maryamariyan modified the milestones: 6.0.0, 7.0.0 Aug 16, 2021
allantargino added a commit to allantargino/dotnet-runtime that referenced this issue Sep 15, 2021
@allantargino
Copy link
Contributor

allantargino commented Sep 15, 2021

Hi, after exploring the problem a bit I believe it relies on the _callSiteCache inside CallSiteFactory:

private readonly ConcurrentDictionary<ServiceCacheKey, ServiceCallSite> _callSiteCache = new ConcurrentDictionary<ServiceCacheKey, ServiceCallSite>();

  • After the first GetService call, it caches the key (IProcessor<int>,0) and the method TryCreateExact sets its value to the IntProcessor's call site.
  • In the second GetService call, when iterating over the first descriptor inside TryCreateEnumerable, the method TryCreateOpenGeneric looks up the cache using the same key, (IProcessor<int>,0), so the existing IntProcessor's call site is returned instead of creating AnyProcessor<int>'s call site.

While we discuss the best approach to solve it, I will send a PR with the failing test for this issue.

eerhardt added a commit that referenced this issue Sep 16, 2021
* adding failing test for issue #57333

* using public api for di callsite test

* Update src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs

Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
@eerhardt eerhardt modified the milestones: 7.0.0, Future Jul 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants