ASP.NET Core Blazor bağımlılığı eklemeASP.NET Core Blazor dependency injection

Tarafından Rainer Stropek ve Mike rousosBy Rainer Stropek and Mike Rousos

Blazorbağımlılık ekleme işlemini (dı)destekler.Blazor supports dependency injection (DI). Uygulamalar, yerleşik hizmetleri ekleme tarafından bileşenlere kullanabilir.Apps can use built-in services by injecting them into components. Uygulamalar Ayrıca özel hizmetleri tanımlayabilir ve kaydedebilir ve bu Hizmetleri uygulama genelinde DI aracılığıyla kullanılabilir hale getirebilirsiniz.Apps can also define and register custom services and make them available throughout the app via DI.

DI, merkezi bir konumda yapılandırılmış hizmetlere erişmek için bir tekniktir.DI is a technique for accessing services configured in a central location. Bu, Blazor uygulamalarda şunları yapmak için yararlı olabilir:This can be useful in Blazor apps to:

  • Hizmet sınıfının tek bir örneğini tek bir hizmet olarak bilinen birçok bileşen arasında paylaşabilirsiniz.Share a single instance of a service class across many components, known as a singleton service.
  • Başvuru soyutlamalarını kullanarak somut hizmet sınıflarından bileşenleri ayırın.Decouple components from concrete service classes by using reference abstractions. Örneğin, IDataAccess uygulamadaki verilere erişim için bir arabirim düşünün.For example, consider an interface IDataAccess for accessing data in the app. Arabirim somut bir sınıf tarafından uygulanır DataAccess ve uygulamanın hizmet kapsayıcısında bir hizmet olarak kaydedilir.The interface is implemented by a concrete DataAccess class and registered as a service in the app's service container. Bir bileşen bir uygulama almak için DI kullandığında IDataAccess , bileşen somut tür ile eşleştirilmez.When a component uses DI to receive an IDataAccess implementation, the component isn't coupled to the concrete type. Uygulama, büyük olasılıkla birim testlerinde bir sahte uygulama için değiştirilebilir.The implementation can be swapped, perhaps for a mock implementation in unit tests.

Varsayılan hizmetlerDefault services

Varsayılan hizmetler, uygulamanın hizmet koleksiyonuna otomatik olarak eklenir.Default services are automatically added to the app's service collection.

HizmetService ÖmürLifetime AçıklamaDescription
HttpClient YayılScoped HTTP istekleri göndermek ve bir URI tarafından tanımlanan bir kaynaktan HTTP yanıtlarını almak için yöntemler sağlar.Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.

HttpClientBir uygulamadaki örneği, Blazor WebAssembly arka planda HTTP trafiğini işlemek için tarayıcıyı kullanır.The instance of HttpClient in a Blazor WebAssembly app uses the browser for handling the HTTP traffic in the background.

Blazor Serveruygulamalar HttpClient Varsayılan olarak yapılandırılmış bir hizmet olarak yapılandırılmamış.Blazor Server apps don't include an HttpClient configured as a service by default. Bir HttpClient Blazor Server uygulamaya bir uygulama sağlayın.Provide an HttpClient to a Blazor Server app.

Daha fazla bilgi için bkz. ASP.NET Core bir Web API 'SI çağırmaBlazor WebAssembly.For more information, see ASP.NET Core bir Web API 'SI çağırmaBlazor WebAssembly.
IJSRuntime Singleton ( Blazor WebAssembly )Singleton (Blazor WebAssembly)
Kapsamlı ( Blazor Server )Scoped (Blazor Server)
JavaScript çağrılarının dağıtıldığı bir JavaScript çalışma zamanının örneğini temsil eder.Represents an instance of a JavaScript runtime where JavaScript calls are dispatched. Daha fazla bilgi için bkz. ASP.NET Core .NET metotlarından JavaScript işlevlerini çağırınBlazor.For more information, see ASP.NET Core .NET metotlarından JavaScript işlevlerini çağırınBlazor.
NavigationManager Singleton ( Blazor WebAssembly )Singleton (Blazor WebAssembly)
Kapsamlı ( Blazor Server )Scoped (Blazor Server)
URI 'Ler ve gezinme durumu ile çalışmaya yönelik yardımcıları içerir.Contains helpers for working with URIs and navigation state. Daha fazla bilgi için bkz. URI ve gezinti durumu yardımcıları.For more information, see URI and navigation state helpers.

Özel bir hizmet sağlayıcı, tabloda listelenen varsayılan Hizmetleri otomatik olarak sağlamaz.A custom service provider doesn't automatically provide the default services listed in the table. Özel bir hizmet sağlayıcısı kullanır ve tabloda gösterilen hizmetlerden herhangi birini gerekliyse, gerekli hizmetleri yeni hizmet sağlayıcısına ekleyin.If you use a custom service provider and require any of the services shown in the table, add the required services to the new service provider.

Uygulamaya hizmet eklemeAdd services to an app

Blazor WebAssembly

Uygulamasındaki uygulamasının hizmet koleksiyonu için Hizmetleri yapılandırın Main Program.cs .Configure services for the app's service collection in the Main method of Program.cs. Aşağıdaki örnekte, MyDependency uygulama için kaydedilir IMyDependency :In the following example, the MyDependency implementation is registered for IMyDependency:

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.Services.AddSingleton<IMyDependency, MyDependency>();
        builder.RootComponents.Add<App>("app");
        
        builder.Services.AddScoped(sp => 
            new HttpClient
            {
                BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
            });

        await builder.Build().RunAsync();
    }
}

Konak oluşturulduktan sonra, herhangi bir bileşen işlenmeden önce kök dı kapsamından hizmetlere erişilebilir.Once the host is built, services can be accessed from the root DI scope before any components are rendered. Bu, içerik işlemeden önce başlatma mantığını çalıştırmak için yararlı olabilir:This can be useful for running initialization logic before rendering content:

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.Services.AddSingleton<WeatherService>();
        builder.RootComponents.Add<App>("app");
        
        builder.Services.AddScoped(sp => 
            new HttpClient
            {
                BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
            });

        var host = builder.Build();

        var weatherService = host.Services.GetRequiredService<WeatherService>();
        await weatherService.InitializeWeatherAsync();

        await host.RunAsync();
    }
}

Konak, uygulama için bir merkezi yapılandırma örneği de sağlar.The host also provides a central configuration instance for the app. Yukarıdaki örnekte derleme yaparken, hava durumu hizmetinin URL 'SI varsayılan bir yapılandırma kaynağından geçirilir (örneğin, appsettings.json ) InitializeWeatherAsync :Building on the preceding example, the weather service's URL is passed from a default configuration source (for example, appsettings.json) to InitializeWeatherAsync:

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.Services.AddSingleton<WeatherService>();
        builder.RootComponents.Add<App>("app");
        
        builder.Services.AddScoped(sp => 
            new HttpClient
            {
                BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
            });

        var host = builder.Build();

        var weatherService = host.Services.GetRequiredService<WeatherService>();
        await weatherService.InitializeWeatherAsync(
            host.Configuration["WeatherServiceUrl"]);

        await host.RunAsync();
    }
}

Blazor Server

Yeni bir uygulama oluşturduktan sonra, yöntemini inceleyin Startup.ConfigureServices :After creating a new app, examine the Startup.ConfigureServices method:

using Microsoft.Extensions.DependencyInjection;

...

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

ConfigureServicesYöntemi IServiceCollection , hizmet açıklayıcı nesnelerinin bir listesi olan bir ( ServiceDescriptor ) iletilir.The ConfigureServices method is passed an IServiceCollection, which is a list of service descriptor objects (ServiceDescriptor). Hizmetler, ConfigureServices hizmet koleksiyonuna hizmet tanımlayıcıları sağlayarak yöntemine eklenir.Services are added in the ConfigureServices method by providing service descriptors to the service collection. Aşağıdaki örnek, IDataAccess arabirimini ve somut uygulamasını içeren kavramı gösterir DataAccess :The following example demonstrates the concept with the IDataAccess interface and its concrete implementation DataAccess:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IDataAccess, DataAccess>();
}

Hizmet ömrüService lifetime

Hizmetler, aşağıdaki tabloda gösterilen ömürlerle yapılandırılabilir.Services can be configured with the lifetimes shown in the following table.

ÖmürLifetime AçıklamaDescription
Scoped Blazor WebAssemblyuygulamalar şu anda bir dı kapsamları kavramı içermez.Blazor WebAssembly apps don't currently have a concept of DI scopes. Scoped-kayıtlı hizmetler hizmetler gibi davranır Singleton .Scoped-registered services behave like Singleton services. Ancak, Blazor Server barındırma modeli Scoped yaşam süresini destekler.However, the Blazor Server hosting model supports the Scoped lifetime. Blazor ServerUygulamalarda, kapsamlı bir hizmet kaydı bağlantınınkapsamına alınır.In Blazor Server apps, a scoped service registration is scoped to the connection. Bu nedenle, geçerli amaç tarayıcıda istemci tarafı çalıştırmak olsa bile, kapsama alınmış hizmetlerin kullanılması geçerli kullanıcı kapsamında olması gereken hizmetler için tercih edilir.For this reason, using scoped services is preferred for services that should be scoped to the current user, even if the current intent is to run client-side in the browser.
Singleton Dı, hizmetin tek bir örneğini oluşturur.DI creates a single instance of the service. Hizmet gerektiren tüm bileşenler Singleton aynı hizmetin bir örneğini alır.All components requiring a Singleton service receive an instance of the same service.
Transient Bir bileşen hizmet kapsayıcısından bir hizmetin örneğini edindiğinde Transient , hizmetin Yeni bir örneğini alır.Whenever a component obtains an instance of a Transient service from the service container, it receives a new instance of the service.

Dı sistemi ASP.NET Core içindeki DI sistemini temel alır.The DI system is based on the DI system in ASP.NET Core. Daha fazla bilgi için bkz. ASP.NET Core'da bağımlılık ekleme.For more information, see ASP.NET Core'da bağımlılık ekleme.

Bir bileşende hizmet istemeRequest a service in a component

Hizmetler hizmet koleksiyonuna eklendikten sonra, @ ekleme yönergesini kullanarak hizmetleri bileşenlere ekleyin Razor .After services are added to the service collection, inject the services into the components using the @inject Razor directive. @injectiki parametreye sahiptir:@inject has two parameters:

  • Tür: eklenecek hizmetin türü.Type: The type of the service to inject.
  • Özellik: eklenen App Service 'i alan özelliğin adı.Property: The name of the property receiving the injected app service. Özelliği el ile oluşturma gerektirmez.The property doesn't require manual creation. Derleyici özelliği oluşturur.The compiler creates the property.

Daha fazla bilgi için bkz. ASP.NET Core görünümlere bağımlılık ekleme.For more information, see ASP.NET Core görünümlere bağımlılık ekleme.

@injectFarklı hizmetler eklemek için birden çok deyim kullanın.Use multiple @inject statements to inject different services.

Aşağıdaki örnek nasıl kullanılacağını göstermektedir @inject .The following example shows how to use @inject. Uygulayan hizmet Services.IDataAccess bileşenin özelliğine eklenir DataRepository .The service implementing Services.IDataAccess is injected into the component's property DataRepository. Kodun yalnızca soyutlamayı nasıl kullandığını aklınızda yapın IDataAccess :Note how the code is only using the IDataAccess abstraction:

@page "/customer-list"
@using Services
@inject IDataAccess DataRepository

@if (customers != null)
{
    <ul>
        @foreach (var customer in customers)
        {
            <li>@customer.FirstName @customer.LastName</li>
        }
    </ul>
}

@code {
    private IReadOnlyList<Customer> customers;

    protected override async Task OnInitializedAsync()
    {
        customers = await DataRepository.GetAllCustomersAsync();
    }
}

Dahili olarak, oluşturulan Özellik ( DataRepository ) özniteliğini kullanır [Inject] .Internally, the generated property (DataRepository) uses the [Inject] attribute. Genellikle, bu öznitelik doğrudan kullanılmaz.Typically, this attribute isn't used directly. Bileşenler için bir temel sınıf gerekliyse ve temel sınıf için eklenen özellikler de gerekliyse, özniteliği el ile ekleyin [Inject] :If a base class is required for components and injected properties are also required for the base class, manually add the [Inject] attribute:

using Microsoft.AspNetCore.Components;

public class ComponentBase : IComponent
{
    [Inject]
    protected IDataAccess DataRepository { get; set; }

    ...
}

Temel sınıftan türetilmiş bileşenlerde, @inject yönergesi gerekli değildir.In components derived from the base class, the @inject directive isn't required. InjectAttributeTaban sınıfının yeterli olması yeterlidir:The InjectAttribute of the base class is sufficient:

@page "/demo"
@inherits ComponentBase

<h1>Demo Component</h1>

Hizmetler 'de dı kullanmaUse DI in services

Karmaşık hizmetler için ek hizmetler gerekebilir.Complex services might require additional services. Önceki örnekte, DataAccess HttpClient varsayılan hizmet gerekebilir.In the prior example, DataAccess might require the HttpClient default service. @inject(veya [Inject] özniteliği) hizmetlerde kullanılamaz.@inject (or the [Inject] attribute) isn't available for use in services. Bunun yerine Oluşturucu Ekleme kullanılmalıdır.Constructor injection must be used instead. Gerekli hizmetler, hizmetin oluşturucusuna parametreler eklenerek eklenir.Required services are added by adding parameters to the service's constructor. Dı hizmeti oluşturduğunda, oluşturucuda gereken hizmetleri algılar ve bunlara göre sağlar.When DI creates the service, it recognizes the services it requires in the constructor and provides them accordingly. Aşağıdaki örnekte, Oluşturucu bir ile bir ile alır HttpClient .In the following example, the constructor receives an HttpClient via DI. HttpClientVarsayılan bir hizmettir.HttpClient is a default service.

public class DataAccess : IDataAccess
{
    public DataAccess(HttpClient client)
    {
        ...
    }
}

Oluşturucu Ekleme önkoşulları:Prerequisites for constructor injection:

  • Bağımsız değişkenlerinin tümü DI tarafından yerine getirilme için tek bir Oluşturucu bulunmalıdır.One constructor must exist whose arguments can all be fulfilled by DI. Varsayılan değerleri belirttiklerinde, DI tarafından kapsanmayan ek parametrelere izin verilir.Additional parameters not covered by DI are allowed if they specify default values.
  • Uygulanabilir Oluşturucu olmalıdır public .The applicable constructor must be public.
  • Uygulanabilir bir Oluşturucu var olmalıdır.One applicable constructor must exist. Belirsizlik söz konusu olduğunda, bir özel durum oluşturur.In case of an ambiguity, DI throws an exception.

Bir dı kapsamını yönetmek için yardımcı program temel bileşen sınıflarıUtility base component classes to manage a DI scope

ASP.NET Core uygulamalarda, kapsamlı hizmetler genellikle geçerli isteğin kapsamlandırılır.In ASP.NET Core apps, scoped services are typically scoped to the current request. İstek tamamlandıktan sonra, tüm kapsamlı veya geçici hizmetler dı sistemi tarafından silinir.After the request completes, any scoped or transient services are disposed by the DI system. Blazor ServerUygulamalarda, istek kapsamı istemci bağlantısı süresince sürer. Bu, geçici ve kapsamlı hizmetlerin beklenenden çok daha uzun süreli olarak oluşmasına neden olabilir.In Blazor Server apps, the request scope lasts for the duration of the client connection, which can result in transient and scoped services living much longer than expected. Blazor WebAssemblyUygulamalarda, kapsamlı bir ömürle kaydedilen hizmetler tekton olarak değerlendirilir, bu nedenle tipik ASP.NET Core uygulamalarında kapsamlı hizmetlerden daha uzun bir süre yaşarlar.In Blazor WebAssembly apps, services registered with a scoped lifetime are treated as singletons, so they live longer than scoped services in typical ASP.NET Core apps.

Not

Bir uygulamada atılabilir geçici Hizmetleri algılamak için geçici disposaı 'Yi Algıla bölümüne bakın.To detect disposable transient services in an app, see the Detect transient disposables section.

Uygulamalarda bir hizmet ömrünü sınırlayan bir yaklaşım Blazor , OwningComponentBase türü kullanır.An approach that limits a service lifetime in Blazor apps is use of the OwningComponentBase type. OwningComponentBase, öğesinden türetilmiş soyut bir türdür ComponentBase ve bileşenin ömrüne karşılık gelen BIR dı kapsamı oluşturur.OwningComponentBase is an abstract type derived from ComponentBase that creates a DI scope corresponding to the lifetime of the component. Bu kapsamı kullanarak, dı hizmetlerini kapsamlı bir ömür ile kullanmak mümkündür ve bileşen olarak bu uygulamaları canlı hale gelir.Using this scope, it's possible to use DI services with a scoped lifetime and have them live as long as the component. Bileşen yok edildiğinde, bileşenin kapsamlı hizmet sağlayıcısından gelen hizmetler de silinir.When the component is destroyed, services from the component's scoped service provider are disposed as well. Bu, şu hizmetler için yararlı olabilir:This can be useful for services that:

  • Geçici ömür uygun olmadığından, bir bileşen içinde yeniden kullanılmalıdır.Should be reused within a component, as the transient lifetime is inappropriate.
  • Tek yaşam süresi uygun olmadığından, bileşenler arasında paylaşılmamalıdır.Shouldn't be shared across components, as the singleton lifetime is inappropriate.

Türün iki sürümü OwningComponentBase kullanılabilir:Two versions of the OwningComponentBase type are available:

  • OwningComponentBase, türünde ComponentBase Protected özelliği olan bir abstract, atılabilir alt öğesidir ScopedServices IServiceProvider .OwningComponentBase is an abstract, disposable child of the ComponentBase type with a protected ScopedServices property of type IServiceProvider. Bu sağlayıcı, bileşenin kullanım ömrü kapsamındaki Hizmetleri çözümlemek için kullanılabilir.This provider can be used to resolve services that are scoped to the lifetime of the component.

    Ya da özniteliği kullanılarak bileşene eklenen dı Hizmetleri, @inject [Inject] bileşen kapsamında oluşturulmaz.DI services injected into the component using @inject or the [Inject] attribute aren't created in the component's scope. Bileşenin kapsamını kullanmak için, hizmetler veya kullanılarak çözümlenmelidir GetRequiredService GetService .To use the component's scope, services must be resolved using GetRequiredService or GetService. Sağlayıcı kullanılarak çözümlenen hizmetlerin, ScopedServices aynı kapsamdaki bağımlılıkları vardır.Any services resolved using the ScopedServices provider have their dependencies provided from that same scope.

    @page "/preferences"
    @using Microsoft.Extensions.DependencyInjection
    @inherits OwningComponentBase
    
    <h1>User (@UserService.Name)</h1>
    
    <ul>
        @foreach (var setting in SettingService.GetSettings())
        {
            <li>@setting.SettingName: @setting.SettingValue</li>
        }
    </ul>
    
    @code {
        private IUserService UserService { get; set; }
        private ISettingService SettingService { get; set; }
    
        protected override void OnInitialized()
        {
            UserService = ScopedServices.GetRequiredService<IUserService>();
            SettingService = ScopedServices.GetRequiredService<ISettingService>();
        }
    }
    
  • OwningComponentBase<TService>öğesinden türetilir OwningComponentBase ve KAPSAMDAKI Service dı sağlayıcısından bir örneğini döndüren bir özellik ekler T .OwningComponentBase<TService> derives from OwningComponentBase and adds a Service property that returns an instance of T from the scoped DI provider. Bu tür, IServiceProvider uygulamanın, bileşenin kapsamını kullanarak dı kapsayıcısından gerektirdiği bir birincil hizmet olduğunda bir örneği kullanmadan kapsamlı hizmetlere erişmenin kolay bir yoludur.This type is a convenient way to access scoped services without using an instance of IServiceProvider when there's one primary service the app requires from the DI container using the component's scope. ScopedServicesÖzelliği kullanılabilir, bu sayede uygulama, gerekirse diğer tür hizmetleri alabilir.The ScopedServices property is available, so the app can get services of other types, if necessary.

    @page "/users"
    @attribute [Authorize]
    @inherits OwningComponentBase<AppDbContext>
    
    <h1>Users (@Service.Users.Count())</h1>
    
    <ul>
        @foreach (var user in Service.Users)
        {
            <li>@user.UserName</li>
        }
    </ul>
    

Dı Entity Framework DbContext kullanımıUse of Entity Framework DbContext from DI

Web Apps 'in kaynağından alınacak bir ortak hizmet türü Entity Framework (EF) DbContext nesneleridir.One common service type to retrieve from DI in web apps is Entity Framework (EF) DbContext objects. Kullanarak EF Services AddDbContext 'in kaydı, DbContext Varsayılan olarak kapsamlı bir hizmet olarak ekler.Registering EF services using AddDbContext adds the DbContext as a scoped service by default. Kapsamlı bir hizmet olarak kaydetme Blazor DbContext , örneklerin uygulama genelinde uzun süreli ve paylaşılan olmasına neden olduğu için uygulamalardaki sorunlara yol açabilir.Registering as a scoped service can lead to problems in Blazor apps because it causes DbContext instances to be long-lived and shared across the app. DbContextiş parçacığı açısından güvenli değildir ve aynı anda kullanılmamalıdır.DbContext isn't thread-safe and must not be used concurrently.

Uygulamaya bağlı olarak, OwningComponentBase bir öğesinin kapsamını tek bir bileşen ile sınırlamak için kullanılması DbContext sorunu çözebilir may .Depending on the app, using OwningComponentBase to limit the scope of a DbContext to a single component may solve the issue. Bir bileşen DbContext paralel olarak kullanmıyorsa, bileşeni öğesinden kaynağından türeterek OwningComponentBase ve DbContext kaynağından alma ScopedServices yeterlidir çünkü şunları sağlar:If a component doesn't use a DbContext in parallel, deriving the component from OwningComponentBase and retrieving the DbContext from ScopedServices is sufficient because it ensures that:

  • Ayrı bileşenler bir paylaşmaz DbContext .Separate components don't share a DbContext.
  • DbContextYalnızca bileşen bu bileşene bağlı olarak sürer.The DbContext lives only as long as the component depending on it.

Tek bir bileşen eşzamanlı olarak kullanılabilir DbContext (örneğin, Kullanıcı her bir düğme seçtiğinde), kullanılması de OwningComponentBase eşzamanlı EF işlemlerinde sorunları önetmez.If a single component might use a DbContext concurrently (for example, every time a user selects a button), even using OwningComponentBase doesn't avoid issues with concurrent EF operations. Bu durumda, DbContext her MANTıKSAL EF işlemi için farklı bir kullanın.In that case, use a different DbContext for each logical EF operation. Aşağıdaki yaklaşımlardan birini kullanın:Use either of the following approaches:

  • DbContext DbContextOptions<TContext> Bir bağımsız değişken olarak kullanarak doğrudan oluşturun, bu, dı 'den alınabilir ve iş parçacığı güvenlidir.Create the DbContext directly using DbContextOptions<TContext> as an argument, which can be retrieved from DI and is thread safe.

    @page "/example"
    @inject DbContextOptions<AppDbContext> DbContextOptions
    
    <ul>
        @foreach (var item in data)
        {
            <li>@item</li>
        }
    </ul>
    
    <button @onclick="LoadData">Load Data</button>
    
    @code {
        private List<string> data = new List<string>();
    
        private async Task LoadData()
        {
            data = await GetAsync();
            StateHasChanged();
        }
    
        public async Task<List<string>> GetAsync()
        {
            using (var context = new AppDbContext(DbContextOptions))
            {
                return await context.Products.Select(p => p.Name).ToListAsync();
            }
        }
    }
    
  • DbContextHizmeti kapsayıcısına geçici bir yaşam süresi ile kaydedin:Register the DbContext in the service container with a transient lifetime:

    • Bağlamı kaydederken kullanın ServiceLifetime.Transient .When registering the context, use ServiceLifetime.Transient. AddDbContextGenişletme yöntemi, türünde iki isteğe bağlı parametre alır ServiceLifetime .The AddDbContext extension method takes two optional parameters of type ServiceLifetime. Bu yaklaşımı kullanmak için yalnızca contextLifetime parametresinin olması gerekir ServiceLifetime.Transient .To use this approach, only the contextLifetime parameter needs to be ServiceLifetime.Transient. optionsLifetimevarsayılan değerini tutabilir ServiceLifetime.Scoped .optionsLifetime can keep its default value of ServiceLifetime.Scoped.

      services.AddDbContext<AppDbContext>(options =>
           options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")),
           ServiceLifetime.Transient);
      
    • Geçici, DbContext @inject paralel olarak bırden çok EF işlemi yürütemeyecek bileşenlere normal (kullanılarak) eklenebilir.The transient DbContext can be injected as normal (using @inject) into components that will not execute multiple EF operations in parallel. Aynı anda birden çok EF işlemi gerçekleştirebilecek olanlar, DbContext kullanarak her paralel işlem için ayrı nesneler isteyebilir GetRequiredService .Those that may perform multiple EF operations simultaneously can request separate DbContext objects for each parallel operation using GetRequiredService.

      @page "/example"
      @using Microsoft.Extensions.DependencyInjection
      @inject IServiceProvider ServiceProvider
      
      <ul>
          @foreach (var item in data)
          {
              <li>@item</li>
          }
      </ul>
      
      <button @onclick="LoadData">Load Data</button>
      
      @code {
          private List<string> data = new List<string>();
      
          private async Task LoadData()
          {
              data = await GetAsync();
              StateHasChanged();
          }
      
          public async Task<List<string>> GetAsync()
          {
              using (var context = ServiceProvider.GetRequiredService<AppDbContext>())
              {
                  return await context.Products.Select(p => p.Name).ToListAsync();
              }
          }
      }
      

Geçici disposleri AlgılaDetect transient disposables

Aşağıdaki örneklerde, kullanması gereken bir uygulamada atılabilir geçici hizmetlerinin nasıl algılanacağı gösterilmektedir OwningComponentBase .The following examples show how to detect disposable transient services in an app that should use OwningComponentBase. Daha fazla bilgi için bkz. BIR dı kapsamı bölümünü yönetmek Için yardımcı program temel bileşen sınıfları .For more information, see the Utility base component classes to manage a DI scope section.

Blazor WebAssembly

DetectIncorrectUsagesOfTransientDisposables.cs:DetectIncorrectUsagesOfTransientDisposables.cs:

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.DependencyInjection
{
    using BlazorServerTransientDisposable;
    using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

    public static class WebHostBuilderTransientDisposableExtensions
    {
        public static WebAssemblyHostBuilder DetectIncorrectUsageOfTransients(
            this WebAssemblyHostBuilder builder)
        {
            builder
                .ConfigureContainer(
                    new DetectIncorrectUsageOfTransientDisposablesServiceFactory());

            return builder;
        }

        public static WebAssemblyHost EnableTransientDisposableDetection(
            this WebAssemblyHost webAssemblyHost)
        {
            webAssemblyHost.Services
                .GetRequiredService<ThrowOnTransientDisposable>().ShouldThrow = true;
            return webAssemblyHost;
        }
    }
}

namespace BlazorServerTransientDisposable
{
    public class DetectIncorrectUsageOfTransientDisposablesServiceFactory 
        : IServiceProviderFactory<IServiceCollection>
    {
        public IServiceCollection CreateBuilder(IServiceCollection services) => 
            services;

        public IServiceProvider CreateServiceProvider(
            IServiceCollection containerBuilder)
        {
            var collection = new ServiceCollection();
            foreach (var descriptor in containerBuilder)
            {
                if (descriptor.Lifetime == ServiceLifetime.Transient &&
                    descriptor.ImplementationType != null && 
                    typeof(IDisposable).IsAssignableFrom(
                        descriptor.ImplementationType))
                {
                    collection.Add(CreatePatchedDescriptor(descriptor));
                }
                else if (descriptor.Lifetime == ServiceLifetime.Transient &&
                         descriptor.ImplementationFactory != null)
                {
                    collection.Add(CreatePatchedFactoryDescriptor(descriptor));
                }
                else
                {
                    collection.Add(descriptor);
                }
            }

            collection.AddScoped<ThrowOnTransientDisposable>();

            return collection.BuildServiceProvider();
        }

        private ServiceDescriptor CreatePatchedFactoryDescriptor(
            ServiceDescriptor original)
        {
            var newDescriptor = new ServiceDescriptor(
                original.ServiceType,
                (sp) =>
                {
                    var originalFactory = original.ImplementationFactory;
                    var originalResult = originalFactory(sp);

                    var throwOnTransientDisposable = 
                        sp.GetRequiredService<ThrowOnTransientDisposable>();
                    if (throwOnTransientDisposable.ShouldThrow && 
                        originalResult is IDisposable d)
                    {
                        throw new InvalidOperationException("Trying to resolve " +
                            $"transient disposable service {d.GetType().Name} in " +
                            "the wrong scope. Use an 'OwningComponentBase<T>' " +
                            "component base class for the service 'T' you are " +
                            "trying to resolve.");
                    }

                    return originalResult;
                },
                original.Lifetime);

            return newDescriptor;
        }

        private ServiceDescriptor CreatePatchedDescriptor(ServiceDescriptor original)
        {
            var newDescriptor = new ServiceDescriptor(
                original.ServiceType,
                (sp) => {
                    var throwOnTransientDisposable = 
                        sp.GetRequiredService<ThrowOnTransientDisposable>();
                    if (throwOnTransientDisposable.ShouldThrow)
                    {
                        throw new InvalidOperationException("Trying to resolve " +
                        "transient disposable service " +
                        $"{original.ImplementationType.Name} in the wrong " +
                        "scope. Use an 'OwningComponentBase<T>' component base " +
                        "class for the service 'T' you are trying to resolve.");
                    }

                    return ActivatorUtilities.CreateInstance(sp, 
                        original.ImplementationType);
                },
                ServiceLifetime.Transient);
            return newDescriptor;
        }
    }

    internal class ThrowOnTransientDisposable
    {
        public bool ShouldThrow { get; set; }
    }
}

TransientDisposableAşağıdaki örnekte algılandı ( Program.cs ):The TransientDisposable in the following example is detected (Program.cs):

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.DetectIncorrectUsageOfTransients();
        builder.RootComponents.Add<App>("app");

        builder.Services.AddTransient<TransientDisposable>();
        builder.Services.AddScoped(sp =>
            new HttpClient
            {
                BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
            });

        var host = builder.Build();
        host.EnableTransientDisposableDetection();
        await host.RunAsync();
    }
}

public class TransientDisposable : IDisposable
{
    public void Dispose() => throw new NotImplementedException();
}

Blazor Server

DetectIncorrectUsagesOfTransientDisposables.cs:DetectIncorrectUsagesOfTransientDisposables.cs:

using System;
using Microsoft.AspNetCore.Components.Server.Circuits;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Microsoft.Extensions.DependencyInjection
{
    using BlazorServerTransientDisposable;

    public static class WebHostBuilderTransientDisposableExtensions
    {
        public static IHostBuilder DetectIncorrectUsageOfTransients(
            this IHostBuilder builder)
        {
            builder
                .UseServiceProviderFactory(
                    new DetectIncorrectUsageOfTransientDisposablesServiceFactory())
                .ConfigureServices(
                    s => s.TryAddEnumerable(ServiceDescriptor.Scoped<CircuitHandler,
                        ThrowOnTransientDisposableHandler>()));

            return builder;
        }
    }
}

namespace BlazorServerTransientDisposable
{
    internal class ThrowOnTransientDisposableHandler : CircuitHandler
    {
        public ThrowOnTransientDisposableHandler(
            ThrowOnTransientDisposable throwOnTransientDisposable)
        {
            throwOnTransientDisposable.ShouldThrow = true;
        }
    }

    public class DetectIncorrectUsageOfTransientDisposablesServiceFactory 
        : IServiceProviderFactory<IServiceCollection>
    {
        public IServiceCollection CreateBuilder(IServiceCollection services) => 
            services;

        public IServiceProvider CreateServiceProvider(
            IServiceCollection containerBuilder)
        {
            var collection = new ServiceCollection();
            foreach (var descriptor in containerBuilder)
            {
                if (descriptor.Lifetime == ServiceLifetime.Transient &&
                    descriptor.ImplementationType != null && 
                    typeof(IDisposable).IsAssignableFrom(
                        descriptor.ImplementationType))
                {
                    collection.Add(CreatePatchedDescriptor(descriptor));
                }
                else if (descriptor.Lifetime == ServiceLifetime.Transient &&
                         descriptor.ImplementationFactory != null)
                {
                    collection.Add(CreatePatchedFactoryDescriptor(descriptor));
                }
                else
                {
                    collection.Add(descriptor);
                }
            }

            collection.AddScoped<ThrowOnTransientDisposable>();

            return collection.BuildServiceProvider();
        }

        private ServiceDescriptor CreatePatchedFactoryDescriptor(
            ServiceDescriptor original)
        {
            var newDescriptor = new ServiceDescriptor(
                original.ServiceType,
                (sp) =>
                {
                    var originalFactory = original.ImplementationFactory;
                    var originalResult = originalFactory(sp);

                    var throwOnTransientDisposable = 
                        sp.GetRequiredService<ThrowOnTransientDisposable>();
                    if (throwOnTransientDisposable.ShouldThrow && 
                        originalResult is IDisposable d)
                    {
                        throw new InvalidOperationException("Trying to resolve " +
                            $"transient disposable service {d.GetType().Name} in " +
                            "the wrong scope. Use an 'OwningComponentBase<T>' " +
                            "component base class for the service 'T' you are " +
                            "trying to resolve.");
                    }

                    return originalResult;
                },
                original.Lifetime);

            return newDescriptor;
        }

        private ServiceDescriptor CreatePatchedDescriptor(
            ServiceDescriptor original)
        {
            var newDescriptor = new ServiceDescriptor(
                original.ServiceType,
                (sp) => {
                    var throwOnTransientDisposable = 
                        sp.GetRequiredService<ThrowOnTransientDisposable>();
                    if (throwOnTransientDisposable.ShouldThrow)
                    {
                        throw new InvalidOperationException("Trying to resolve " +
                            "transient disposable service " +
                            $"{original.ImplementationType.Name} in the wrong " +
                            "scope. Use an 'OwningComponentBase<T>' component " +
                            "base class for the service 'T' you are trying to " +
                            "resolve.");
                    }

                    return ActivatorUtilities.CreateInstance(sp, 
                        original.ImplementationType);
                },
                ServiceLifetime.Transient);
            return newDescriptor;
        }
    }

    internal class ThrowOnTransientDisposable
    {
        public bool ShouldThrow { get; set; }
    }
}

Program:Program:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .DetectIncorrectUsageOfTransients()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

TransientDependencyAşağıdaki örnekte algılandı ( Startup.cs ):The TransientDependency in the following example is detected (Startup.cs):

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddSingleton<WeatherForecastService>();
    services.AddTransient<TransientDependency>();
    services.AddTransient<ITransitiveTransientDisposableDependency, 
        TransitiveTransientDisposableDependency>();
}

public class TransitiveTransientDisposableDependency 
    : ITransitiveTransientDisposableDependency, IDisposable
{
    public void Dispose() { }
}

public interface ITransitiveTransientDisposableDependency
{
}

public class TransientDependency
{
    private readonly ITransitiveTransientDisposableDependency 
        _transitiveTransientDisposableDependency;

    public TransientDependency(ITransitiveTransientDisposableDependency 
        transitiveTransientDisposableDependency)
    {
        _transitiveTransientDisposableDependency = 
            transitiveTransientDisposableDependency;
    }
}

Ek kaynaklarAdditional resources