如何在 Startup class 之外访问 IServiceCollection and/or IServiceProvider?
How to access IServiceCollection and/or IServiceProvider outside of Startup class?
我在 .Net Farmework 4.6.2 class 库中使用 Microsoft.Extensions.DependencyInjection。如何从实例化它们的源代码外部访问 IServiceCollection and/or IServiceProvider,例如 Main()?我可以创建一个静态 class 在单独的 class 库中公开此 属性 并引用它,但我想知道是否有任何其他更好的方法来实现此 objective。 .Net Framework 有 ServiceLocator.Current.GetInstance。 Microsoft.Extensions.DependencyInjection有没有类似的东西?感谢任何建议和见解。
没有单例"Instance"。您可以根据需要创建任意数量的不同服务提供者。您可以将其作为参数正常传递。
您可以将 注入 IServiceProvider
到任何由它实例化的 class 中。只需将其添加为构造函数参数即可。
大多数时候,IServiceProvider
不应该直接使用,因为它会导致Service Locator antipattern而不是依赖注入模式。
但是,在某些情况下它确实有意义。特别是在多线程 WPF 或 Blazor 应用程序中,您需要多个范围的数据库访问。为此,您只需将 IServiceProvider
注入从中创建的 class 的构造函数中:
public sealed class ServiceProviderResolver
{
public ServiceProviderResolver(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public IServiceProvider ServiceProvider { get; }
}
然后您可以根据需要创建新的作用域服务:
public static IDisposable Scoped<T>(this IServiceProvider scopeFactory, out T context)
{
var scope = scopeFactory.CreateScope();
var services = scope.ServiceProvider;
context = services.GetRequiredService<T>();
return scope;
}
示例:WPF 用户控件
假设我们使用:
public partial class MyWPFControl : UserControl, IViewFor<MyWPFControlViewModel>
{
private IServiceProvider ServiceProvider { get; }
public MyWPFControl()
{
InitializeComponent();
// Use dependency resolver to get the service provider
ServiceProvider = Splat.Locator.Current
.GetService<ServiceProviderResolver>()
.ServiceProvider;
this.WhenActivated(d => {
ActivateCustomerIdSelected(d);
// ...
});
}
private void ActivateCustomerIdSelected(d)
{
this.WhenAnyValue(vm => vm.CustomerId)
.Where(id => id != null)
.Select(_ => this)
.SelectMany(async (vm, ct) => {
// Make a database query in a dedicated scope
// This way we can isolate the context
// Such that other threads won't interfer with the context
using(var scope = ServiceProvider.Scoped<IMyDbContext>(out var context))
{
return await context.Customers
.Where(c => c.Id == vm.CustomerId)
.AsNoTracking()
.SingleOrDefaultAsync(ct)
.ConfigureAwait(false);
}
})
.ObserveOn(RxApp.MainThreadScheduler)
// execute the DB query on the TaskPool
.SubscribeOn(RxApp.TaskPoolScheduler)
// Handle the result
// Note: we are still on the task pool here
// you might need DynamicData.SourceCache
// or DynamicData.SourceList when handling
// multiple results
.Subscribe(customer => { /* ... */ })
.DisposeWith(d);
}
}
// ...
[Reactive] public Guid? CustomerId { get; set; }
// ...
}
与 IServiceProvider
不同,IServiceCollection
不能注入到 class 构造函数中。
正如许多人所说,您应该避免直接使用它们。但是如果你真的需要为IServiceCollection
这样做,你可以创建一个“Provider”,例如:
public interface IServiceCollectionProvider
{
IServiceCollection ServiceCollection { get; }
}
public sealed class ServiceCollectionProvider: IServiceCollectionProvider
{
public ServiceCollectionProvider(IServiceCollection serviceCollection)
{
ServiceCollection = serviceCollection;
}
public IServiceCollection ServiceCollection { get; }
}
正在注册:
services.AddSingleton<IServiceCollectionProvider>(new ServiceCollectionProvider(services));
使用:
public class YourController : Controller
{
private readonly IServiceProvider _provider;
private readonly IServiceCollection _services;
public YourController (IServiceProvider provider, IServiceCollectionProvider serviceCollectionProvider)
{
_provider = provider;
_services = serviceCollectionProvider.ServiceCollection;
}
}
我在 .Net Farmework 4.6.2 class 库中使用 Microsoft.Extensions.DependencyInjection。如何从实例化它们的源代码外部访问 IServiceCollection and/or IServiceProvider,例如 Main()?我可以创建一个静态 class 在单独的 class 库中公开此 属性 并引用它,但我想知道是否有任何其他更好的方法来实现此 objective。 .Net Framework 有 ServiceLocator.Current.GetInstance。 Microsoft.Extensions.DependencyInjection有没有类似的东西?感谢任何建议和见解。
没有单例"Instance"。您可以根据需要创建任意数量的不同服务提供者。您可以将其作为参数正常传递。
您可以将 注入 IServiceProvider
到任何由它实例化的 class 中。只需将其添加为构造函数参数即可。
大多数时候,IServiceProvider
不应该直接使用,因为它会导致Service Locator antipattern而不是依赖注入模式。
但是,在某些情况下它确实有意义。特别是在多线程 WPF 或 Blazor 应用程序中,您需要多个范围的数据库访问。为此,您只需将 IServiceProvider
注入从中创建的 class 的构造函数中:
public sealed class ServiceProviderResolver
{
public ServiceProviderResolver(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public IServiceProvider ServiceProvider { get; }
}
然后您可以根据需要创建新的作用域服务:
public static IDisposable Scoped<T>(this IServiceProvider scopeFactory, out T context)
{
var scope = scopeFactory.CreateScope();
var services = scope.ServiceProvider;
context = services.GetRequiredService<T>();
return scope;
}
示例:WPF 用户控件
假设我们使用:
public partial class MyWPFControl : UserControl, IViewFor<MyWPFControlViewModel>
{
private IServiceProvider ServiceProvider { get; }
public MyWPFControl()
{
InitializeComponent();
// Use dependency resolver to get the service provider
ServiceProvider = Splat.Locator.Current
.GetService<ServiceProviderResolver>()
.ServiceProvider;
this.WhenActivated(d => {
ActivateCustomerIdSelected(d);
// ...
});
}
private void ActivateCustomerIdSelected(d)
{
this.WhenAnyValue(vm => vm.CustomerId)
.Where(id => id != null)
.Select(_ => this)
.SelectMany(async (vm, ct) => {
// Make a database query in a dedicated scope
// This way we can isolate the context
// Such that other threads won't interfer with the context
using(var scope = ServiceProvider.Scoped<IMyDbContext>(out var context))
{
return await context.Customers
.Where(c => c.Id == vm.CustomerId)
.AsNoTracking()
.SingleOrDefaultAsync(ct)
.ConfigureAwait(false);
}
})
.ObserveOn(RxApp.MainThreadScheduler)
// execute the DB query on the TaskPool
.SubscribeOn(RxApp.TaskPoolScheduler)
// Handle the result
// Note: we are still on the task pool here
// you might need DynamicData.SourceCache
// or DynamicData.SourceList when handling
// multiple results
.Subscribe(customer => { /* ... */ })
.DisposeWith(d);
}
}
// ...
[Reactive] public Guid? CustomerId { get; set; }
// ...
}
与 IServiceProvider
不同,IServiceCollection
不能注入到 class 构造函数中。
正如许多人所说,您应该避免直接使用它们。但是如果你真的需要为IServiceCollection
这样做,你可以创建一个“Provider”,例如:
public interface IServiceCollectionProvider
{
IServiceCollection ServiceCollection { get; }
}
public sealed class ServiceCollectionProvider: IServiceCollectionProvider
{
public ServiceCollectionProvider(IServiceCollection serviceCollection)
{
ServiceCollection = serviceCollection;
}
public IServiceCollection ServiceCollection { get; }
}
正在注册:
services.AddSingleton<IServiceCollectionProvider>(new ServiceCollectionProvider(services));
使用:
public class YourController : Controller
{
private readonly IServiceProvider _provider;
private readonly IServiceCollection _services;
public YourController (IServiceProvider provider, IServiceCollectionProvider serviceCollectionProvider)
{
_provider = provider;
_services = serviceCollectionProvider.ServiceCollection;
}
}