ASP.NET Core MVC привязка FromService в контроллере возвращает пустую коллекцию

Старался захватить ASP.NET Core MVC, но застрял в инъекции зависимостей, возвращая пустую коллекцию в действие моего контроллера, несмотря на регистрацию службы как ConfigureServiceметод Singleton в методе. Пробовал два разных способа регистрации, но только один из них действительно работает. Указанный способ заключается в следующем:

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();
    /***THIS DOES NOT WORK***/
    services.AddSingleton<IRepository<Employee>, EmployeeRepository>();
    var empRepo = services.BuildServiceProvider().GetService<IRepository<Employee>>();
    empRepo.Create( "emp001" );

    /***THIS WORKS***/
    var employeeRepository = new EmployeeRepository();
    employeeRepository.Create( "emp001" );
    services.AddSingleton<EmployeeRepository>( employeeRepository );
}

И следующее действие моего контроллера:

[HttpGet( "{empID}" )]
public string GetEmployee( string empID, [FromServices] IRepository<Employee> employees **/*THIS IS ALWAYS EMPTY*/**)
{
    ....
}

Просьба указать мне в правильном направлении. В чем разница между этими двумя AddSingletonметодами? Как я могу получить доступ к сервису с использованием 1-го подхода с помощью DI на действии Controller? ТИА.

c#,dependency-injection,asp.net-core,singleton,asp.net-core-mvc,

1

Ответов: 3


1

Как реализован ваш репозиторий? Предполагаю, что вы вводите в нее новый EmployeeRepository ()? Затем вы не можете использовать его DbContextдля создания экземпляра, и вы не можете использовать singleton. DbContextразрешается для каждой области видимости, и поэтому все сервисы, которые ее используют, должны делать это.

Также никогда не звоните services.BuildServiceProvider()внутри ConfigureServices, потому что прежде, чем вызов ConfigureASP.NET Webstack будет делать это в любом случае и создаст нового поставщика, не связанного с тем, который вы создали ранее.

В-третьих, никогда не разрешайте облачные службы через область приложения (при вызове app.ApplicationServices.GetService<T>()это может вызвать проблемы со сроком службы вашей службы. Если во время запуска приложения вы разрешаете услугу с ограниченным доступом (то есть посев данных), создайте новую область, разрешите ее, затем удалите его когда-то.


1

Поскольку в действии вашего контроллера вам нужен экземпляр IRepository<Employee>, вы можете использовать любой из этих двух методов, если зарегистрированная услуга относится к этому типу (в вашем втором подходе вы регистрировали его как EmployeeRepository):

services.AddSingleton<IRepository<Employee>, EmployeeRepository>();
services.AddSingleton<IRepository<Employee>>(new EmployeeRepository());

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

  • При использовании первого варианта экземпляр будет создан при первом запуске службы в каком-либо классе.
  • Со вторым вариантом экземпляр создается вручную вами в ConfigureServices.

Какой из них следует использовать, зависит от ваших потребностей.

Что касается того, почему вы получили его как null, в вашем проекте должно быть что-то еще, проверьте наличие ошибок в консоли или попытайтесь воспроизвести проблему в новом минимальном проекте. Я попытался воспроизвести его, но оба метода регистрации работали нормально:

  • Новое веб-приложение 1.0.1
  • Затем добавили следующие типы:

    public class Employee { }
    public interface IRepository<T> { }
    public class EmployeeRepository: IRepository<Employee> { }
  • Обновлено действие Index в HomeController для получения экземпляра, используя [FromServices]:

    public IActionResult Index([FromServices]IRepository<Employee> emp)

РЕДАКТИРОВАТЬ

Как упоминалось в @Tseng , вы не должны вызывать BuildServiceProviderметод Startup:

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

Либо оставьте фактическое создание экземпляра поставщику услуг, либо используйте второй AddSingletonметод, если вам нужно создать экземпляр самостоятельно. Не беспокойтесь, если ваш класс также нуждается в сервисах в своем конструкторе, поставщик услуг будет нуждаться в том, как их разрешать, если они были зарегистрированы в Startup.

Например, обновите свое репо, чтобы сохранить в памяти статический список сотрудников:

public class EmployeeRepository : IRepository<Employee>
{
    private List<string> employees = new List<string>();
    public void Create(string employeeId)
    {
        this.employees.Add(employeeId);
    }
}

Затем добавьте нового сотрудника каждый раз, когда вы вызываете действие индекса:

public IActionResult Index([FromServices]IRepository<Employee> repo)
{
    repo.Create($"client-{ Guid.NewGuid() }");
    return View();
}

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

Имейте в виду еще один комментарий @Tseng , где он упоминает, что одноэлемент, вероятно, не самый лучший вариант для репозитория. Обычно это зависит от некоторого DbContext, который создается для каждого запроса. (Если вы не создаете какую-либо форму поставщика DbContext, и ваше репо зависит от него, но это не по теме)


0 принят

Я должен был вернуться к единственному рабочему варианту для меня, что и предложил @Daniel JG ; AddSingleton<T>()после инициализации репозитория (наряду с некоторыми полезными советами от него). Хотя, мне хотелось бы, чтобы другая функция работала после инициализации.

C #, зависимость инъекции, asp.net-ядро, синглтон, asp.net-ядро-MVC,
Похожие вопросы