ASP.NET Core DI ile örnekleri çözme


303

ASP.NET Core MVC yerleşik bağımlılık enjeksiyon çerçevesini kullanarak bir türü el ile nasıl çözebilirim?

Konteyneri kurmak yeterince kolaydır:

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

Ancak ISomeServiceenjeksiyon yapmadan nasıl çözebilirim ? Örneğin, bunu yapmak istiyorum:

ISomeService service = services.Resolve<ISomeService>();

'De böyle bir yöntem yoktur IServiceCollection.



3
Bunları ConfigureServices()yöntemde (ile IServiceCollection) veya uygulamanın herhangi bir yerinde çözmek istiyor musunuz ?
Henk Mollema

2
@HenkMollema: Başlangıçta herhangi bir yerde.
Dave New

Yanıtlar:


489

IServiceCollectionArayüzü için kullanılan yapı , bir bağımlılık enjeksiyon kap. Tamamen oluşturulduktan sonra, IServiceProviderhizmetleri çözmek için kullanabileceğiniz bir örneğe göre oluşturulur. IServiceProviderHerhangi bir sınıfa bir enjekte edebilirsiniz . IApplicationBuilderVe HttpContextsınıflar ile, hem servis sağlayıcı sağlayabilir ApplicationServicesya da RequestServicessırasıyla özellikleri.

IServiceProviderGetService(Type type)bir servisi çözmek için bir yöntem tanımlar :

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

Ayrıca, serviceProvider.GetService<IFooService>()( usingfor a ekle Microsoft.Extensions.DependencyInjection) gibi çeşitli kolaylık genişletme yöntemleri de vardır .

Başlangıç ​​sınıfı içindeki hizmetleri çözümleme

Enjeksiyon bağımlılıkları

Çalışma zamanının barındırma hizmeti sağlayıcısı yapıcı içine belirli hizmetleri enjekte Startupgibi sınıf, IConfiguration, IWebHostEnvironment( IHostingEnvironment3.0'dan sürümlerinde), ILoggerFactoryve IServiceProvider. İkincisinin barındırma katmanı tarafından oluşturulan bir örnek olduğunu ve yalnızca bir uygulamayı başlatmak için gerekli hizmetleri içerdiğini unutmayın .

ConfigureServices()Yöntem sadece bir kabul, hizmetlerini enjekte izin vermez IServiceCollectionargüman. Bu mantıklıdır çünkü ConfigureServices()uygulamanızın gerektirdiği hizmetleri kaydettiğiniz yer burasıdır. Ancak, burada başlangıç ​​yapıcısına enjekte edilen hizmetleri kullanabilirsiniz, örneğin:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

Kayıtlı herhangi bir hizmet ConfigureServices()daha sonra Configure()yönteme enjekte edilebilir ; şu IApplicationBuilderparametreden sonra isteğe bağlı sayıda hizmet ekleyebilirsiniz :

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

public void Configure(IApplicationBuilder app, IFooService fooService)
{
    fooService.Bar();
}

Bağımlılıkları elle çözme

Hizmetleri el ile çözümlemeniz gerekiyorsa, tercihen aşağıdaki yöntemle ApplicationServicessağlananları kullanmalısınız :IApplicationBuilderConfigure()

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

Sınıfınızın IServiceProvideryapıcısında bir geçmek ve doğrudan kullanmak mümkündür Startup, ancak yukarıdaki gibi bu sınırlı bir hizmet alt kümesi içerecektir ve bu nedenle sınırlı faydaya sahiptir:

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}

ConfigureServices()Yöntemdeki hizmetleri çözümlemeniz gerekiyorsa , farklı bir yaklaşım gerekir. Sen bir ara inşa edebilirsiniz IServiceProviderdan IServiceCollectiontescil edilmiştir hizmetlerini içeren örneğine o noktaya kadar :

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();

    // This will succeed.
    var fooService = sp.GetService<IFooService>();
    // This will fail (return null), as IBarService hasn't been registered yet.
    var barService = sp.GetService<IBarService>();
}

Lütfen dikkat: Genellikle uygulama içindeki hizmetleri yapılandırdığınızConfigureServices() yer olduğu için hizmetleri yöntem içinde çözmekten kaçınmalısınız . Bazen sadece bir örneğe erişmeniz gerekir . Bunu örnekten bir örneğe bağlayarak (esas olarak seçenekler çerçevesinin yaptığı şeydir) bunu yapabilirsiniz:IOptions<MyOptions>IConfigurationMyOptions

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

Hizmetleri manuel olarak çözümlemek (Servis Bulucu olarak da bilinir) genellikle bir anti-desen olarak kabul edilir . Kullanım durumları olsa da (çerçeveler ve / veya altyapı katmanları için) mümkün olduğunca kaçınmalısınız.


14
@HenkMollema ama enjekte bir şey yapamıyorsam, yani enjekte edemiyorum IServiceCollection, manuel olarak yaratılan bir sınıf ( orta eşya kapsamı dışında ), benim durumumda periyodik olarak oluşturmak için bazı hizmetlere ihtiyaç duyan bir zamanlayıcı ve bir e-posta gönderin.
Merdan Gochmuradov

52
hizmetleri çözmeniz gerekiyorsa ConfigureServicesve bu hizmet bir singleton ise uyarı , kullandığınızdan farklı bir singleton olacaktır Controller! Ben farklı bir kullandığından bu olduğunu varsayalım IServiceProvideryoluyla çözmek OLMAYAN Bunu önlemek için - BuildServiceProviderve yerine gelen tekil sizin arama taşımak ConfigureServicesiçin Configure(..other params, IServiceProvider serviceProvider)deStartup.cs
wal

3
@wal iyi bir nokta. Farklı bir IServiceProviderörnek olduğu için yeni bir singleton örneği oluşturacaktır. ConfigureServicesUygulamanızın kullandığı kapsayıcı olacak şekilde hizmet sağlayıcı örneğini yöntemden döndürerek bundan kaçınabilirsiniz .
Henk Mollema

1
Çağırmak collection.BuildServiceProvider();ihtiyacım olan şeydi, teşekkürler!
Chris Marisic

2
@HenkMollema sadece bir servis sağlayıcı örneği ile nasıl çalışmasını sağlar? Genellikle, 1) Bağımlılıklarınızın bir kısmını kaydedin 2) geçici bir hizmet sağlayıcı örneği oluşturun 3) Başka bir bağımlılığı kaydetmek için ihtiyacınız olan bir şeyi çözmek için bu hizmet sağlayıcıyı kullanın. Daha sonra, bazı bağımlılıklarınızı (3'te kayıtlı) eksik olduğu için geçici örneği döndüremezsiniz. Bir şey mi kaçırıyorum?
Filip

110

Örneklerin manuel olarak çözülmesi, IServiceProviderarayüzün kullanılmasını içerir :

Başlangıçta Bağımlılığı Çözme.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

Başlangıçtaki Bağımlılıkları Çözme.Configure

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

ASP.NET Core 3'te Başlangıçtaki Bağımlılıkları Çözme.

public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    app.ApplicationServices.GetService<MyService>();
}

Çalışma Zamanı Enjekte Edilmiş Hizmetlerini Kullanma

Bazı tipler yöntem parametreleri olarak enjekte edilebilir:

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

Denetleyici Eylemlerindeki Bağımlılıkları Çözme

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";

1
@AfsharMohebbi GetServicegenel olan Microsoft.Extensions.DependencyInjectionad alanında bir uzantı yöntemidir .
ahmadali shafiee

Genişletme Yöntemleri Hakkında: Genişletme yöntemi, bir sınıfa işlev ekleyen statik bir yöntemdir, genel statik TheReturnType TheMethodName (bu TheTypeYouExtend theTypeYouExtend {// BODY} 'u bildirebilir ve daha sonra bunu kullanabilirsiniz: TheTypeYouExtend.TheMethodName (); .NET Core ile çok yaygın bir yaklaşım haline gelir, böylece geliştiriciler temel işlevselliği genişletebilirler ... burada iyi örnekler: docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
Juan

17

Şablon içeren bir uygulama oluşturursanız, Startupsınıfta şöyle bir şey olacaktır :

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

Daha sonra buraya bağımlılıklar ekleyebilirsiniz, örneğin:

services.AddTransient<ITestService, TestService>();

ITestServiceKontrol cihazınızdan erişmek istiyorsanız, kurucuya ekleyebilirsiniz IServiceProviderve enjekte edilir:

public HomeController(IServiceProvider serviceProvider)

Ardından eklediğiniz hizmeti çözebilirsiniz:

var service = serviceProvider.GetService<ITestService>();

Genel sürümü kullanmak için, uzantıları içeren bir ad alanı eklemeniz gerektiğini unutmayın:

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs (ConfigureServices)

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddMvc();

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }

10

Bir bağımlılığı, kaydettirmekte olduğunuz başka bir bağımlılığın yapıcısına iletmek amacıyla çözmeniz gerekiyorsa, bunu yapabilirsiniz.

Diyelim ki bir dize ve bir ISomeService alan bir hizmetiniz var.

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

Bunu Startup.cs içine kaydettirdiğinizde bunu yapmanız gerekir:

services.AddScoped<IAnotherService>(ctx => 
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);

OP, ConfigureService yönteminde bir servisi çözme
gereğini

1
Aslında bu kabul edilen cevap olmalı ... Henk Mollema'nın cevabı çok açıklayıcı olmasına rağmen, günümüzde cevabınız daha temiz ve ara IServiceProvider (farklı tekil örnekler ...) oluşturma ile ilgili sorunlar getirmiyor. Muhtemelen, bu çözüm Henk'in boğulduğu 2015'te mevcut değildi, ama şimdi gitmek için bir yol.
Vi100

Bunu denedim ama ISomeServicebenim için hala boştu.
19:39

2 soru: 1) Servis sınıfı AnotherService parametre yapıcı değişirse (kaldırılan veya eklenen hizmetler), o zaman hizmet IAnotherService kayıt segmentini değiştirmek gerekir ve sürekli değişiyor mu? 2) Bunun yerine, AnotherService (IServiceProvider serviceProvider) gibi 1 parametreli AnotherService için yalnızca bir yapıcı ekleyebilir ve yapıcıdan ihtiyacım olan hizmetleri alabilirim. Ve ben sadece servis sınıfı gibi AnotherService hizmetlerini Startup sınıfında kaydetmeliyim.
Thomas.Benz

2

AuthorizeAttribute gibi özelliklerde bağımlılıkları bu şekilde enjekte edebilirsiniz.

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));

Aradığım şey bu .. Teşekkürler
Reyan Chougle

0

Bunun eski bir soru olduğunu biliyorum ama burada oldukça açık ve iğrenç bir hack'in bulunmadığından şaşkınım.

Siz tanımlarken hizmetlerinizden gerekli değerleri elde etmek için kendi ctor işlevinizi tanımlama yeteneğinden yararlanabilirsiniz ... bu hizmet sömürücünün ilk inşaatı içinde .

Bu yöntemin avantajı, hizmetin yapılandırılması sırasında hizmet ağacını oluşturmanızı veya kullanmamanızı gerektirmemektedir. Hala hizmetlerin nasıl yapılandırılacağını tanımlıyorsunuz.

public void ConfigureServices(IServiceCollection services)
{
    //Prey this doesn't get GC'd or promote to a static class var
    string? somevalue = null;

    services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
         //create service you need
         var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
         //get the values you need
         somevalue = somevalue ?? service.MyDirtyHack();
         //return the instance
         return service;
    });
    services.AddTransient<IOtherService, OtherService>(scope => {
         //Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
         scope.GetService<IServiceINeedToUse>();
         //TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
         //Wow!
         return new OtherService(somevalue);
    });
}

Bu modeli düzeltmenin yolu , örtük olarak ona veya yönteminin dönüş değerine bağlı olmak yerine OtherServiceaçık bir bağımlılık vermektir IServiceINeedToUse... veya bu bağımlılığı başka bir şekilde açık bir şekilde çözmektir.


-4
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}

5
Sadece bir kod snippet'inin değil , neden iyi bir yanıt olduğunu kısa bir şekilde açıklarsanız, yanıtlarınızın kabul edilmesi ve onaylanması daha olasıdır . Ayrıca, askerin bunun aslında sordukları soruya cevap verdiğinden emin olmasına yardımcı olur.
Jim L

Birisi cevabınızı yanlış bir şekilde düşük kaliteli olarak işaretledi. Daha fazla işaretlemeyi ve / veya aşağı oyu önlemek için cevabınızın nasıl çalıştığını açıklamak için eşlik eden bir metin eklemelisiniz. Yalnızca kod yanıtı düşük kalitede değildir . Soruyu cevaplamaya çalışıyor mu? Değilse, 'yanıt değil' olarak işaretleyin veya silinmesini önerin (inceleme sırasındaysa). b) Teknik olarak yanlış mı? Aşağı oy veya yorum. İncelemeden .
Wai Ha Lee
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.