Зачем нужен IServiceProvider
Существует такой тип в .NET как IServiceProvider. Его описание в официальной документации очень расплывчато:
This interface is implemented by a class or value type that provides a service to other objects.
The GetService method of this interface obtains the object that provides the service.
Я знаю, что этот тип используется в ASP.NET и при создании кастомного XAML расширения.
Но где ещё может использоваться этот тип? Можно ли ему найти свой применение в каких-либо других областях, как на продакшене, так и просто для себя? Да и какая вообще логика в использовании типа как аргумента метода GetService?
Ответы (1 шт):
IServiceProvider это интерфейс позволяющий вам использовать внедрение зависимостей в ваш код.
Как правильно написал @Mark Shevchenko для полного понимания хорошо почитать книжку " Внедрение зависимостей в .NET".
Но в целом это работает так.
Например у вас есть класс, который зависит от данных находящихся в каком-то хранилище, доступ к которому осуществляется через какие-то промежуточные классы. При обычном программировании он выглядит примерно так:
class MsSqlDataAccessor
{
public DbDataAccessor() {/*тут логика инициализации*/}
public IList<MyCoolEntity> GetAll()
{
//тут много кода специфичного для вашей бд.
}
}
class Summarizer
{
public Summarizer() {/*тут логика инициализации*/}
public GetSum()
{
var dataAccessor = new MsSqlDataAccessor();
var result = 0;
foreach(var item in dataAccessor.GetAll()){
result += item.Sum;
}
}
}
Если у вас возникает необходимость протестировать такой код в юнит-тестах, то вам необходимо создать дополнительную базу данных и использовать ее в тестах. Если вам нужно использовать другую базу данных или web-сервис в качестве источника данных, в зависисмости от настроек, вам нужно будет добавлять if/else в каждое место где создается экземпляр MsSqlDataAccessor, что бы создать экземпляр правильного класса. Это очень сильно усложнит код.
Что бы упростить жизнь используется IServiceProvider и IServiceCollection. IServiceCollection используется для определения списка доступных сервисов. IServiceProvider используется для извлечения необходимых вам сервисов из коллекции.
Пример с теми же классами.
public interface IDataAccessor
{
IList<MyCoolEntity> GetAll();
}
class MsSqlDataAccessor : IDataAccessor
{
public DbDataAccessor() {/*тут логика инициализации*/}
public IList<MyCoolEntity> GetAll()
{
//тут много кода специфичного для вашей бд.
}
}
class MockDataAccessor : IDataAccessor
{
public DbDataAccessor() {/*тут логика инициализации*/}
public IList<MyCoolEntity> GetAll()
{
var result = new List<MyCoolEntity>();
result.Add(new MyCoolEntity()
{
Sum = 100;
});
result.Add(new MyCoolEntity()
{
Sum = 200;
});
return result;
}
}
class Summarizer
{
private readonly IServiceProvider _serviceProvider;
public Summarizer(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
/*тут логика инициализации*/
}
public GetSum()
{
var dataAccessor = _serviceProvider.GetService<IDataAccessor>();
var result = 0;
foreach(var item in dataAccessor.GetAll()){
result += item.Sum;
}
}
}
А где-то в инициализации приложения, скорее всего в Startup.cs или специально выделенном классе есть такой код:
Services.AddScoped<IDataAccessor, MsSqlDataAccessor>(); // Здесь Services - экземпляр класса реализующего IServiceCollection
А в юнит-тесте есть такой код:
var services = new ServiceCollection();
var mockAccessor = new MockDataAccessor();
services.AddScoped<IServiceProvider, MsSqlDataAccessor>()
var summarizer = new Summarizer(services.BuildServiceProvider());
var result = summarizer.GetSum();
Assert.Equals(300, result);