मूल प्रदाता .net Core 2 से स्कोप की गई सेवा को हल नहीं कर सकता


92

जब मैं अपना ऐप चलाने की कोशिश करता हूं तो मुझे त्रुटि मिलती है

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

यह अजीब है कि यह EmailRepository और इंटरफ़ेस बिल्कुल समान है जहाँ तक मैं अपने सभी अन्य रिपॉजिटरी के रूप में बता सकता हूँ, फिर भी उनके लिए कोई त्रुटि नहीं है। त्रुटि तभी होती है जब मैं ऐप का उपयोग करने की कोशिश करता हूं। 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/D 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();
}

मुझे नहीं पता कि वास्तव में क्या चल रहा है लेकिन यह अब काम करने लगता है।


4
वहाँ क्या हो रहा है, यह है कि गुंजाइश घोंसले के शिकार को मान्य नहीं किया जा रहा है; जैसा कि, यह रनटाइम के दौरान जाँच नहीं कर रहा है, यदि आपके पास स्कोप स्तर के अनुचित घोंसले के शिकार हैं। जाहिर है, यह 1.1 में डिफ़ॉल्ट रूप से बंद कर दिया गया था। एक बार 2.0 के साथ आने के बाद, उन्होंने इसे डिफ़ॉल्ट रूप से चालू कर दिया।
रॉबर्ट बर्क

ValidateScope को बंद करने का प्रयास करने वाले किसी व्यक्ति के लिए, कृपया इस stackoverflow.com/a/50198738/1027250
योरो

जवाबों:


188

आपने IEmailRepositoryएक स्कोप सेवा के रूप में, Startupकक्षा में पंजीकृत किया । इसका मतलब है कि आप इसे एक कंस्ट्रक्टर पैरामीटर के रूप में इंजेक्ट नहीं कर सकते Middlewareक्योंकि इसमें केवल Singletonकंस्ट्रक्टर इंजेक्शन द्वारा सेवाओं को हल किया जा सकता है 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);
    }
}

13
वाह! कभी नहीं पता था कि आप तरीकों में इंजेक्शन लगा सकते हैं, क्या यह सिर्फ मिडलवेयर के लिए है या क्या मैं इस ट्रिक का इस्तेमाल अपने तरीकों से कर सकता हूं?
फ़र्गल मोरन

IMiddleware कि scoped के रूप में पंजीकृत है के बारे में क्या? मुझे पता है कि मुझे मिडलवेयर का एक नया उदाहरण मिलता है, लेकिन मैं अभी भी इसे एक स्कोप सेवा को इंजेक्ट नहीं कर सकता।
बोटिस ऑक्ट

2
@FergalMoran दुर्भाग्य से, यह "ट्रिक" सिर्फ मिडलवेयर की Invokeविधि का एक विशेष व्यवहार है । हालाँकि, आप स्वतः पूर्ण IoC lib और गुण इंजेक्शन के माध्यम से कुछ समान प्राप्त कर सकते हैं। संपत्ति या सेटर विधि के माध्यम से ASP.NET Core MVC निर्भरता इंजेक्शन देखें ?
B12Toaster

4
इंजेक्शन जादू नहीं है। पर्दे के पीछे एक इंजन है जो वास्तव में निर्भरता कंटेनर को निर्माणकर्ताओं या विधियों के मापदंडों के रूप में पारित करने के लिए उदाहरण उत्पन्न करने के लिए कहता है। यह पार्टिकल इंजन HttpContext के पहले तर्क के साथ "इनवोक" नाम के तरीकों की तलाश करता है और फिर बाकी मापदंडों के लिए उदाहरण बनाता है।
थानिस इयोनिडिस

97

एक और तरीका है दायरे वाले निर्भरता का उदाहरण प्राप्त करने के लिए सेवा प्रदाता (सुई है IServiceProviderमिडलवेयर निर्माता में), बनाने scopeमें Invokeविधि और फिर दायरे से आवश्यक सेवा मिलती है:

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

    //do your stuff....
}

अधिक जानकारी के लिए asp.net कोर निर्भरता इंजेक्शन सर्वोत्तम प्रथाओं युक्तियाँ चाल में एक विधि निकाय में सेवाओं को सुलझाने की जाँच करें ।


5
सुपर सहायक, धन्यवाद! ईएफ़ कॉन्टेक्ट्स को मिडलवेयर में एक्सेस करने की कोशिश करने वाले किसी भी व्यक्ति के लिए यह रास्ता है क्योंकि वे डिफ़ॉल्ट रूप से स्कूप होते हैं।
ntziolis

stackoverflow.com/a/49886317/502537 इसे और अधिक सीधे करता है
RickAndMSFT

पहले तो मुझे नहीं लगा कि यह काम किया है, लेकिन तब मुझे महसूस हुआ कि आप दूसरी पंक्ति के scope.ServiceProviderबजाय कर रहे हैं _serviceProvider। इसके लिए धन्यवाद।
adam0101

_serviceProvider.CreateScope ()। ServiceProvider मेरे लिए बेहतर है
XLR8

मुझे लगता है कि IServiceScopeFactoryइस उद्देश्य के लिए उपयोग करना सबसे अच्छा होगा
फ्रांसेस्को डीएम

27

मिडलवेयर हमेशा एक सिंगलटन होता है ताकि आप अपने मिडलवेयर के कंस्ट्रक्टर में कंस्ट्रक्टर निर्भरता के रूप में निर्भरता को न छोड़े।

मिडलवेयर इनवोक विधि पर विधि इंजेक्शन का समर्थन करता है, इसलिए आप बस IEmailRepository emailRepository को उस पद्धति के पैरामीटर के रूप में जोड़ सकते हैं और इसे वहां इंजेक्ट किया जाएगा और स्कोप के रूप में ठीक होगा।

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

    ....
}

मैं भी ऐसी ही स्थिति में था, तब मैंने AddTransient का उपयोग करके सेवा को जोड़ा, और यह निर्भरता को हल करने में सक्षम था। मैंने सोचा कि यह काम नहीं करेगा क्योंकि मिडलवेयर सिंगलटन है? थोड़ा अजीब ..
सतेश पगोलू

1
मुझे लगता है कि एक क्षणिक निर्भरता को मैन्युअल रूप से निपटाया जाना होगा, स्कॉप्ड के विपरीत जो कि वेब अनुरोध के अंत में स्वचालित रूप से निपटाया जाएगा जहां यह पहली बार बनाया गया है। हो सकता है कि एक स्कोप्ड निर्भरता के अंदर एक क्षणिक डिस्पोजेबल हो जाता है और बाहरी वस्तु का निपटान हो जाता है। फिर भी मुझे यकीन नहीं है कि एक सिंगलटन के अंदर एक क्षणिक निर्भरता या क्षणिक जीवनकाल की तुलना में लंबे समय तक एक वस्तु एक अच्छा विचार है, मुझे लगता है कि मैं इससे बचूंगा।
जो ऑडिट

2
भले ही आप इस मामले में कंस्ट्रक्टर के माध्यम से एक क्षणिक-स्कोप की निर्भरता को इंजेक्ट कर सकते हैं, लेकिन यह तुरंत नहीं मिलेगा जैसा आप सोचते हैं। यह केवल एक बार होगा, जब सिंगलटन बनाया जाएगा।
जोनाथन

1
आपने उल्लेख किया है कि मिडलवेयर हमेशा एक सिंगलटन होता है, लेकिन यह सच नहीं है। फैक्ट्री-आधारित मिडलवेयर के रूप में मिडिलवेयर बनाना और स्कोप-मिडिलवेयर के रूप में उपयोग करना संभव है।
हरुन दिलुका हशन

ऐसा लगता है कि कारखाना आधारित मिडलवेयर एस्पार्टकोर 2.2 में पेश किया गया था और 2019 में दस्तावेज तैयार किया गया था। इसलिए जब तक मैं जानता हूं, तब तक मेरा जवाब सही था। फैक्टरी आधारित मिडलवेयर आज एक अच्छे समाधान की तरह दिखता है।
जो ऑडिट

4

आपका middlewareऔर serviceक्रम में सुई एक दूसरे के साथ संगत हो गया है serviceके माध्यम से constructorअपने की middleware। यहाँ, आपका निर्माण middlewareकिया गया है convention-based middlewareजिसका अर्थ है कि यह एक के रूप में कार्य करता है singleton serviceऔर आपने अपनी सेवा को बनाया है scoped-service। इसलिए, आप scoped-serviceकंस्ट्रक्टर में इंजेक्ट नहीं कर सकते singleton-serviceक्योंकि यह scoped-serviceएक के रूप में कार्य करने के लिए मजबूर करता है singleton। हालाँकि, यहाँ आपके विकल्प हैं।

  1. आपकी सेवा को एक पैरामीटर के रूप में इंजेक्ट करें InvokeAsyncविधि के ।
  2. यदि संभव हो तो अपनी सेवा को एक सिंगलटन बनाएं।
  3. अपने middlewareको एक में बदलना factory-based

A एक के Factory-based middlewareरूप में कार्य करने में सक्षम है scoped-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
{
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.