aspnet / DependencyInjection

[Archived] Contains common DI abstractions that ASP.NET Core and Entity Framework Core use. Project moved to https://github.com/aspnet/Extensions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scoped Dependency Injection failed

audacity76 opened this issue · comments

commented

Hi,

I was directed here to ask my question again. I have a scoped service which is normally injected in all other services once per request. Only the implemented ISqlServerConnection which is created by a DbContext with its internal service provider gives me a new instance of my service. Any helpful tips?

How did it fail? Do you have a stack trace? Do you have a minimal repro? What does the code look like?

commented

I doesn't fail, it gives me a new instance of the service but not the scoped one.

Code:


public class ContextService 
{
    public string TenantCode { get; set; }
}
public class ApplicationSqlServerConnection : SqlServerConnection
{
    protected ContextService ContextService;

    public ApplicationSqlServerConnection(IDbContextOptions options, ILogger<SqlServerConnection> logger, ContextService contextService)
        : base(options, logger)
    {
        ContextService = contextService;
    }

    public override void Open()
    {
        base.Open();
        //Login using ContextService.TenantCode, but it is null
    }

    public override async Task OpenAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        await base.OpenAsync(cancellationToken);
        //Login using ContextService.TenantCode, but it is null
    }
}

Startup ConfigureServices()
...
services
    .AddEntityFrameworkSqlServer()
    .AddDbContext<AppDbContext>((serviceProvider, options) => options
        .UseSqlServer(
            sqlServerConnectionString,
            sqlServerOptions => sqlServerOptions.EnableRetryOnFailure())
        .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning))
        .UseInternalServiceProvider(serviceProvider));
...
services.AddScoped<ISqlServerConnection, ApplicationSqlServerConnection>();
services.AddScoped<ContextService>();
...
public class HomeController
{
    private AppDbContext _DbContext;
    private ContextService _ContextService;
    
    public HomeController(AppDbContext dbContext, ContextService contextService)
    {
       _DbContext = dbContext;
        _ContextService = contextService;
    }
   
   public IActionResult Index()
   {
         _ContextService.TenantCode = "0815";
        _DbContext.SomeData.ToList(); // Fire the SqlServerConnection.Open()
   }
}

I'm assuming here, but is it possible that there are actually 2 different scopes being created?

@ajcvickers Does EF create it's own scope when activating the ISqlServerConnection?

@audacity76 You can't resolve EF's internal services like this. They are created in a scope that is managed by the DbContext. Your HomeController is not created in that scope. You really shouldn't try to mix EF's internal services with your application's services like this. That's one of the reasons we recommend strongly against calling UseInternalServiceProvider--as you have seen, even if you try to combine the service providers like this it usually doesn't work in the way you expect. If you really need access to EF's internal services, then the best thing to do is call DbContext.GetService<>().

commented

@ajcvickers How can I replace service ISqlServerConnection if I remove UseInternalServiceProvider()?

@audacity76 Use ReplaceService on the DbContextOptionsBuilder.

commented

@ajcvickers Ok. Now it finds the new implementation but the dependency injection inside the ApplicationSqlServerConnection doesn't work then. Service Provider doesn't find my ContextService. How can I configure this?

This really isn't a DI issue 😄

@audacity76 In 2.0, if you need application services inside of an EF service, then you can use context.GetService for this.

commented

@ajcvickers So, no chance in 1.1?

@audacity76 It is likely possible to do with enough hacking of the internals, but I wouldn't recommend it. This ability was added to 2.0 specifically because it was messy to do it any other way.

Closing this for now because I don't think there is anything actionable here.

commented

@ajcvickers After migrating to 2.1 I started a new attempt on this but still I can't get my scoped ContextService instance. Can you please further explain how/where I have to use context.GetService