Проблемы установки контейнера Lamar IoC
Я пытался заставить Ламара работать на нашу инъекцию зависимости, но мой недостаток опыта заставил меня испытать некоторое горе.
У меня есть простой фиктивный контроллер:
[Route("[controller]")]
public class TestController : Controller
{
[HttpGet]
public int GetRandom()
{
TestService service = new TestService();
int value = service.GetRandomNumber();
return value;
}
}
Внутри TestService находится репозиторий интерфейса, в котором я хотел бы применить DI.
Я добавил UseLamar в мой Program.cs, и мой startup.cs выглядит так:
public void ConfigureContainer(ServiceRegistry services)
{
services.AddMvc();
services.Scan(s =>
{
s.Assembly("Test.Components");
s.WithDefaultConventions();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync("Invalid");
});
app.UseMvc();
}
Из прочтения документации я считаю, что Lamar должен автоматически сопоставить мой интерфейс с моим типом, так как они имеют одинаковое имя (IRepository -> Repository), хотя это не то, что я вижу.
Я уверен, что что-то пропустил, но я перебрал документацию и пытался поискать реальные примеры безрезультатно. Есть ли строки, которые мне не хватает в Startup.cs? Нужен ли моему Сервису конструктор для установки репозитория?
1 ответ
Вообще говоря, вы хотите внедрить свои зависимости через конструктор. Есть случаи, когда вы могли бы использовать другой подход, но в моем опыте более чем 90% случаев - это внедрение через конструктор.
Таким образом, вы действительно хотите добавить IRepository в ваш конструктор TestService. Однако из вашего кода похоже, что вам не хватает чего-то более фундаментального в том, как работает внедрение зависимостей.
TestService service = new TestService();
Когда вы используете new для создания объекта, вы управляете жизненным циклом и областью действия этого объекта. Смысл использования инфраструктуры внедрения зависимостей состоит в том, чтобы позволить ей (структуре / контейнеру) управлять зависимостями и их областью действия / жизненным циклом. Если вы создаете экземпляр объекта с помощью контейнера внедрения зависимостей, он разрешает зависимости этого объекта для вас - он также управляет зависимостями этих зависимостей, и так далее, так что вы можете иметь цепочку зависимостей, которые разрешаются без проработка сложностей инициализации большого графа объектов вручную (используя "новый").
Чтобы решить ваш непосредственный вопрос, вы можете сделать что-то вроде (это плохой код - не рекомендуется!):
TestService service = container.Resolve<ITestService>();
Контейнер будет контейнером lamar, где вы регистрируете свои типы - я не знаком с lamar, поэтому синтаксис Resolve() может выглядеть немного иначе. Это разрешило бы TestService и добавило бы репозиторий и все аргументы конструктора класса TestService.
Предпочтительный способ сделать это - разрешить цепочку зависимостей на границе (точке входа) вашего приложения, которым является сам ваш контроллер. Таким образом, ваш контроллер должен принять ITestService в качестве аргумента конструктора, а контейнер / инфраструктура разрешит TestController => TestService => Репозиторий.
Вам нужно немного поработать, чтобы это заработало. Как я уже сказал, я не знаю lamar, поэтому я не уверен, как заставить это работать с Asp.net Core.
В Asp.Net Framework WebAPI это выглядело бы примерно так с использованием Unity для DI:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new UnityControllerActivator(container));
Этот код запускается при запуске приложения и сообщает платформе ASP.Net: при поступлении запроса разрешите соответствующий контроллер и все его зависимости, используя предоставленный контейнер.