IEnumerable<IRepository> возвращает другой экземпляр, чем при запросе (например) IUserRepository

В настоящее время я пытаюсь предварительно загрузить некоторые данные. Детали для него не важны, но структура выглядит примерно так:

public interface IRepository
{
    public Task PreLoadData();
}

public interface IUserRepository : IRepository
{
    public List<User> Users { get; }
    // other instance specific methods.
}

public class UserRepository : IUserRepository
{
    public List<User> Users { get; private set; }

    public async Task PreLoadData()
    {
        Users = await GetUsers();
    }
}

Когда я регистрирую их как services.AddScoped<IRepository, UserRepository>(), в класс можно добавить и IEnumerable<IRepository>, и IUserRepository. Однако; это отдельные экземпляры: при вызове .PreLoadData() члена IEnumerable<IRepository> данные не загружаются, когда я позже внедряю конкретную версию интерфейса. Однако если я введу весь IEnumerable, все эти члены заполнятся. Я также пытался зарегистрироваться как Singleton, но это тоже не сработало.

Когда я регистрирую их как services.AddScoped<IUserRepository, UserRepository>(), IEnumerable<IRepository> остаётся пустым.

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

public class DataPreLoader
{
    private IEnumerable<IRepository> _repositories;

    public DataPreLoader(IEnumerable<IRepository> repositories)
    {
        _repositories = repositories;
    }

    public async Task PreloadAllData()
    {
        foreach(var repository in _repositories)
        {
            await repository.PreLoadData();
        }
    }
}

Могу ли я в любом случае убедиться, что IEnumerable<IDataLoader> получает те же экземпляры, что и введенные при использовании IUserRepository?

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

Не имеет отношения к делу, но вы уверены, что хотите этого? Мне кажется, что это вариант использования предварительно разогретого кеша.

Fildor 10.06.2024 13:56

Это тоже хорошее предложение, я еще об этом не подумал!

Nijenhof 10.06.2024 14:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда я регистрирую их как services.AddScoped<IRepository, UserRepository>(), в класс можно внедрить как IEnumerable, так и IUserRepository.

Это неправда. Благодаря этой регистрации вы могли разрешить IRepository или IEnumerable<IRepository> от поставщика услуг, но не IUserRepository.

Чтобы иметь возможность сделать это, вам также необходимо иметь регистрацию services.AddScoped<IUserRepository, UserRepository>(), но это совершенно отдельная регистрация службы с точки зрения контейнера службы.

Следствием этого является то, что вы получите другой экземпляр в той же области, в зависимости от того, разрешили ли вы IUserRepository или IRepository (или IEnumerable<IRepository>, если уж на то пошло). Тот факт, что тип реализации один и тот же, не имеет значения.

Например:

var serviceProvider = new ServiceCollection()
    .AddScoped<IRepository, UserRepository>()
    .AddScoped<IUserRepository, UserRepository>()
    .BuildServiceProvider()
    .CreateScope().ServiceProvider;

var a = serviceProvider.GetRequiredService<IRepository>();
var b = serviceProvider.GetRequiredService<IEnumerable<IRepository>>();
var c = serviceProvider.GetRequiredService<IUserRepository>();

a == b.Single(); // true
a == c // false

Чтобы добиться того, что вам нужно, вы можете зарегистрировать конкретный тип реализации (UserRepository) и фабричные функции для разрешения из каждого интерфейса, например.

new ServiceCollection()
    .AddScoped<UserRepository>()
    .AddScoped<IRepository, UserRepository>(
        sp => sp.GetRequiredService<UserRepository>())
    .AddScoped<IUserRepository, UserRepository>(
        sp => sp.GetRequiredService<UserRepository>());

Прекрасно, это имеет смысл для меня. Я надеялся, что будет способ сделать это только с одной регистрацией, но, к сожалению, это не так.

Nijenhof 10.06.2024 13:30

Другие вопросы по теме