[c#] 루트 공급자 .Net Core 2에서 범위가 지정된 서비스를 확인할 수 없습니다.

내 앱을 실행하려고하면 오류가 발생합니다.

InvalidOperationException: Cannot resolve 'API.Domain.Data.Repositories.IEmailRepository' from root provider because it requires scoped service 'API.Domain.Data.EmailRouterContext'.

이상한 점은이 EmailRepository와 인터페이스가 다른 모든 저장소와 동일하게 설정되어 있지만 오류가 발생하지 않는다는 것입니다. 이 오류는 app.UseEmailingExceptionHandling ();을 사용하려고 할 때만 발생합니다. 선. 다음은 내 Startup.cs 파일 중 일부입니다.

public class Startup
{
    public IConfiguration Configuration { get; protected set; }
    private APIEnvironment _environment { get; set; }

    public Startup(IConfiguration configuration, IHostingEnvironment env)
    {
        Configuration = configuration;

        _environment = APIEnvironment.Development;
        if (env.IsProduction()) _environment = APIEnvironment.Production;
        if (env.IsStaging()) _environment = APIEnvironment.Staging;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        var dataConnect = new DataConnect(_environment);

        services.AddDbContext<GeneralInfoContext>(opt => opt.UseSqlServer(dataConnect.GetConnectString(Database.GeneralInfo)));
        services.AddDbContext<EmailRouterContext>(opt => opt.UseSqlServer(dataConnect.GetConnectString(Database.EmailRouter)));

        services.AddWebEncoders();
        services.AddMvc();

        services.AddScoped<IGenInfoNoteRepository, GenInfoNoteRepository>();
        services.AddScoped<IEventLogRepository, EventLogRepository>();
        services.AddScoped<IStateRepository, StateRepository>();
        services.AddScoped<IEmailRepository, EmailRepository>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole();

        app.UseAuthentication();

        app.UseStatusCodePages();
        app.UseEmailingExceptionHandling();

        app.UseMvcWithDefaultRoute();
    }
}

다음은 EmailRepository입니다.

public interface IEmailRepository
{
    void SendEmail(Email email);
}

public class EmailRepository : IEmailRepository, IDisposable
{
    private bool disposed;
    private readonly EmailRouterContext edc;

    public EmailRepository(EmailRouterContext emailRouterContext)
    {
        edc = emailRouterContext;
    }

    public void SendEmail(Email email)
    {
        edc.EmailMessages.Add(new EmailMessages
        {
            DateAdded = DateTime.Now,
            FromAddress = email.FromAddress,
            MailFormat = email.Format,
            MessageBody = email.Body,
            SubjectLine = email.Subject,
            ToAddress = email.ToAddress
        });
        edc.SaveChanges();
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
                edc.Dispose();
            disposed = true;
        }
    }
}

마지막으로 예외 처리 미들웨어

public class ExceptionHandlingMiddleware
{
    private const string ErrorEmailAddress = "errors@ourdomain.com";
    private readonly IEmailRepository _emailRepository;

    private readonly RequestDelegate _next;

    public ExceptionHandlingMiddleware(RequestDelegate next, IEmailRepository emailRepository)
    {
        _next = next;
        _emailRepository = emailRepository;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex, _emailRepository);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception,
        IEmailRepository emailRepository)
    {
        var code = HttpStatusCode.InternalServerError; // 500 if unexpected

        var email = new Email
        {
            Body = exception.Message,
            FromAddress = ErrorEmailAddress,
            Subject = "API Error",
            ToAddress = ErrorEmailAddress
        };

        emailRepository.SendEmail(email);

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int) code;
        return context.Response.WriteAsync("An error occured.");
    }
}

public static class AppErrorHandlingExtensions
{
    public static IApplicationBuilder UseEmailingExceptionHandling(this IApplicationBuilder app)
    {
        if (app == null)
            throw new ArgumentNullException(nameof(app));
        return app.UseMiddleware<ExceptionHandlingMiddleware>();
    }
}

업데이트 :이 링크 https://github.com/aspnet/DependencyInjection/issues/578 을 발견하여 Program.cs 파일의 BuildWebHost 메서드를 변경했습니다.

public static IWebHost BuildWebHost(string[] args)
{
    return WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .Build();
}

이에

public static IWebHost BuildWebHost(string[] args)
{
    return WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseDefaultServiceProvider(options =>
            options.ValidateScopes = false)
        .Build();
}

정확히 무슨 일이 일어나고 있는지 모르겠지만 지금은 작동하는 것 같습니다.



답변

클래스 IEmailRepository에서 범위 서비스로을 등록했습니다 Startup. 즉, .NET에서 생성자 삽입으로 서비스 MiddlewareSingleton해결할 수 있으므로 에서 생성자 매개 변수로 삽입 할 수 없습니다 Middleware. 종속성을 다음 Invoke과 같이 메서드 로 이동해야 합니다.

public ExceptionHandlingMiddleware(RequestDelegate next)
{
    _next = next;
}

public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{
    try
    {
        await _next.Invoke(context);
    }
    catch (Exception ex)
    {
        await HandleExceptionAsync(context, ex, emailRepository);
    }
}


답변

범위의 의존성의 인스턴스를 얻는 또 다른 방법은 서비스 제공자 (주입하는 IServiceProvider미들웨어 생성자) 생성 scopeInvoke있어서 다음 범위에서 필요한 서비스를받을 :

using (var scope = _serviceProvider.CreateScope()) {
    var _emailRepository = scope.ServiceProvider.GetRequiredService<IEmailRepository>();

    //do your stuff....
}

자세한 내용 은 asp.net 핵심 종속성 주입 모범 사례 팁 트릭 에서 메서드 본문의 서비스 해결을 확인 하십시오.


답변

미들웨어는 항상 싱글 톤이므로 미들웨어 생성자에서 생성자 종속성으로 범위 종속성을 가질 수 없습니다.

미들웨어는 Invoke 메서드에 대한 메서드 삽입을 지원하므로 IEmailRepository emailRepository를 해당 메서드에 매개 변수로 추가하기 만하면 해당 메서드에 삽입되고 범위가 지정됩니다.

public async Task Invoke(HttpContext context, IEmailRepository emailRepository)
{

    ....
}


답변

당신 middleware과는 service를 주입하기 위해 서로 호환되어야합니다 service를 통해 constructor당신의 middleware. 여기에서 귀하 middleware는으로 생성되었으며 convention-based middleware이는으로 작동하고 singleton service서비스를 scoped-service. 그래서, 당신은을 주입 할 수 scoped-servicea의 생성자 singleton-service는이 강제 때문에 scoped-serviceA와 역할을 singleton하나. 그러나 여기에 옵션이 있습니다.

  1. 서비스를 매개 변수로 InvokeAsync메소드에 삽입하십시오 .
  2. 가능하면 서비스를 싱글 톤으로 만드십시오.
  3. 당신 middlewarefactory-based하나로 바꾸십시오 .

A Factory-based middlewarescoped-service. 따라서 scoped-service해당 미들웨어의 생성자를 통해 다른 것을 주입 할 수 있습니다 . 아래에서 factory-based미들웨어 를 만드는 방법을 보여 드렸습니다.

이것은 데모 용입니다. 그래서 다른 모든 코드를 제거했습니다.

public class Startup
{
    public Startup()
    {
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<TestMiddleware>();
        services.AddScoped<TestService>();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMiddleware<TestMiddleware>();
    }
}

TestMiddleware:

public class TestMiddleware : IMiddleware
{
    public TestMiddleware(TestService testService)
    {
    }

    public Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        return next.Invoke(context);
    }
}

TestService:

public class TestService
{
}


답변