Не может использовать сервис с ограниченным доступом xxx от синглтона ггг - PullRequest
1 голос
/ 24 марта 2019

Я последовал за публикацией в блоге «Создание вашего первого веб-API с помощью ASP.NET Core и кода Visual Studio».

http://www.codingflow.net/building-your-first-web-api-with-asp-net-core-and-visual-studio-code/

В этом случае данные сохраняются не в базе данных, а в памяти, например:

services.AddDbContext<TodoContext>(options => options.UseInMemoryDatabase());
services.AddSingleton<ITodoRepository, TodoRepository>();

Вы заметите:

(1) UseInMemoryDatabase на DbContext

(2) AddSingleton на TodoRepository

Это работает довольно хорошо. Теперь я обновил код для сохранения данных внутри реальной базы данных . Итак, основные изменения:

services.AddDbContext<TodoContext> (options => options.UseSqlite("Data Source=blogging.db"));            
services.AddSingleton<ITodoRepository, TodoRepository>();

Я хотел бы сообщить, что мне пришлось мигрировать AspNetCore с 1.0 до 2.2.

Теперь во время выполнения, при нацеливании на контроллер, я получил ошибку: Невозможно использовать сервис с областью действия 'Models.TodoContext' из синглтона 'Models.ITodoRepository'.

Я понимаю, что в этой ситуации:

  • My TodoContext - это объект Scoped: одинаковый в запросе, но различный для разных запросов.

  • My TodoRepository является объектом Singleton: одинаково для каждого объекта и каждого запроса.

Так что я наконец-то изменил AddSingleton на AddScoped, который работает довольно хорошо:

services.AddDbContext<TodoContext> (options => options.UseSqlite("Data Source=blogging.db"));            
services.AddScoped<ITodoRepository, TodoRepository>();

Мой вопрос : узнать, является ли это приемлемым подходом?

PS: я знаю, что есть другие вопросы по этому вопросу на SO, но я не прочитал четкий ответ.

Ответы [ 2 ]

0 голосов
/ 24 марта 2019

Ядро ASP.NET, встроенное в контейнер внедрения зависимостей, защищает вас от анти-паттерна внедрения зависимостей, называемого «зависимыми зависимостями» (вы можете узнать больше об этом и о внедрении зависимостей в целом здесь ).

Основная идея состоит в том, что класс, имеющий определенное время жизни, может зависеть только от объектов, имеющих время жизни, равное или большее, чем его собственное время жизни.Это происходит потому, что когда вы делаете внедрение зависимостей, вы предоставляете класс со всеми его зависимостями (обычно через внедрение конструктора), и этот класс сохраняет ссылку на зависимость, чтобы он мог использовать ее позже, когда это необходимо.

Поскольку проектируемый вами класс сохраняет ссылку на внедренный объект, внедренный объект остается живым (по крайней мере) до тех пор, пока экземпляр вашего класса жив.Может быть, пример поможет вам понять.

public interface IFoo {}

public class Foo: IFoo {}

public class Bar 
{
  private readonly IFoo foo;

  public Bar(IFoo foo)
  {
    this.foo = foo ?? throw new ArgumentNullException(nameof(foo));
  }
}

var foo = new Foo();
var bar = new Bar(foo); // the object referenced by foo variable is alive as long as the Bar instance is alive, because a reference to it is saved inside the private field of Bar instance

Этот сценарий может создать вам проблемы, если предполагаемое время жизни экземпляров Foo меньше, чем предполагаемое время жизни экземпляров Bar.

Например, представьте, что находитесь в контексте веб-приложения, и предположим, что класс Foo не является потокобезопасным, так что доступ к его экземпляру одновременно из разных потоков может привести к повреждению его частного состояния.,В этом сценарии вы можете зарегистрировать класс Foo как зависимую область, чтобы каждый раз, когда приложение получало HTTP-запрос, создавался новый экземпляр Foo, и этот экземпляр использовался в течение всего времени существования HTTP-запроса.Это нормально, даже если обработка HTTP-запроса подразумевает использование некоторых асинхронных операций.Предположим, что вы ожидаете каждой задействованной асинхронной операции, может быть только один поток, одновременно обращающийся к вашему экземпляру Foo, и внутреннее состояние экземпляра сохраняется от повреждения.

При таком сценарии, если вы зарегистрируете класс Bar как одноэлементный, тогда может существовать только один экземпляр Bar за все время существования приложения, и поэтому разные потоки будут одновременно обращаться к экземпляру Bar (помните, что вебприложение может обслуживать несколько запросов одновременно, используя пул потоков).Ваш одноэлементный экземпляр Bar имеет ссылку на экземпляр Foo, который потенциально может использоваться одновременно из нескольких потоков, что приведет к повреждению его внутреннего состояния и непредсказуемым результатам.У вас есть зависимая зависимость, потому что у вас есть синглтон (класс Bar), который зависит от класса с более коротким временем жизни (класс Foo с областью действия).

Возвращаясь к вашему вопросу, ваше решение хорошо: вы не можете зарегистрировать свой репозиторий как одиночный, потому что он должен использовать зависимую область, поэтому, по моему мнению, регистрация в качестве сервисной области является хорошим подходом.

0 голосов
/ 24 марта 2019

На ваш вопрос, да, это правильный способ сделать вещи.

Я хотел бы обобщить ответ лучше, но затем я провел некоторое исследование по этой теме, и я нашел статья , в которой рассматриваются вопросы, подобные тем, которые вы задавали.

Я бы посоветовал взглянуть на эту статью, поскольку в ней есть разделы "Лучшие практики", посвященные решению определенных проблем.

...