В настоящее время я пытаюсь предварительно загрузить некоторые данные. Детали для него не важны, но структура выглядит примерно так:
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. В качестве примера я использовал репозиторий, так как мне показалось, что большинство с этим знакомы. В настоящей реализации это совсем другое. Поэтому комментарии о том, что данные не загружаются из базы данных таким способом, хотя и приветствуются, не будут необходимы )
Это тоже хорошее предложение, я еще об этом не подумал!





Когда я регистрирую их как
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>());
Прекрасно, это имеет смысл для меня. Я надеялся, что будет способ сделать это только с одной регистрацией, но, к сожалению, это не так.
Не имеет отношения к делу, но вы уверены, что хотите этого? Мне кажется, что это вариант использования предварительно разогретого кеша.