如何使用 DryIoc 解决 WebAPI 控制器的依赖关系
How to resolve the dependencies of a WebAPI Controller with DryIoc
我一直在通读文档,对于如何实现这一点我有点困惑。我有一个名为 NewsController 的 WebAPI 控制器,我将在下面列出。它有一个构造函数,目前需要三个依赖项。我为 DryIoc 创建了一个静态 class,这样我就可以在整个项目中全局使用它,并且它会在启动时进行初始化。
我想做的是在我的 DryIoc class 中注册控制器及其依赖项,然后以某种方式在 NewsController 控制器中解决它。我想这样做的原因是,当我开始测试时,我可以简单地让测试项目更改已注册控制器的范围并使用存根或模拟实现。
RegisterDependencies
public static class RegisterDependencies
{
public static Container container;
public static void Initialize()
{
container = new Container(rules => rules
.WithDefaultReuseInsteadOfTransient(Reuse.InWebRequest)
.WithoutThrowOnRegisteringDisposableTransient()
.WithoutImplicitCheckForReuseMatchingScope());
container.Register<INewsManager, NewsManager>();
container.Register<IGetNews, NewsManager>();
container.Register<IAddNews, NewsManager>();
container.Register<ILoggingService, Logger>();
container.Register<NewsController>(made: Made.Of(() => new NewsController
(Arg.Of<ILoggingService>(), Arg.Of<IGetNews>(), Arg.Of<IAddNews>(),
Arg.Of<INewsManager>())));
}
}
NewsController(无论如何都是一部分)
public class NewsController : ApiController
{
private INewsManager _nm;
private IGetNews _getNews;
private IAddNews _addNews;
private ILoggingService _log;
public NewsController(ILoggingService logger, IGetNews getNews,
IAddNews addNews, INewsManager newsManager)
{
_getNews = getNews;
_addNews = addNews;
_log = logger;
_nm = newsManager;
}
[HttpGet]
public IHttpActionResult GetNews()
{
var newsItems = _getNews.GetNews();
if (newsItems == null || newsItems.Count() <= 0)
{
_log.Error("News Items couldn't be loaded.");
return NotFound();
}
return Ok(Mapper.Map<List<NewsDto>>(newsItems));
}
更新:
到目前为止,我通过 WebApiConfig 进行了 "automatic" 依赖解析,但我不确定如何从测试项目访问它,以便我可以用我的存根替换真实的实现。
WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RegisterDependencies.controllerContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
config, throwIfUnresolved: type => type.IsController());
RegisterDependencies.InitializeControllerContainer(RegisterDependencies.controllerContainer);
}
RegisterDependencies
public static class RegisterDependencies
{
public static IContainer controllerContainer;
public static void InitializeControllerContainer(IContainer ControllerContainer)
{
ControllerContainer.RegisterMany<ILoggingService, Logger>(setup: Setup.With(allowDisposableTransient: true));
ControllerContainer.RegisterMany<INews, NewsManager>(setup: Setup.With(allowDisposableTransient: true));
}
NewsController
public class NewsController : ApiController
{
private INews _news;
private ILoggingService _log;
public NewsController(INews news, ILoggingService logger)
{
_news = news;
_log = logger;
}
好的,这就是我的想法。
在上面 UPDATE: 下的问题中,您将看到使用 WebAPIConfig 自动注册和解析依赖项的代码。您需要使用 NuGet 和 DryIoc 中的 DryIoc.WebAPI 包。
为了能够替换测试实现,您需要为 test/mock 依赖项创建一个 class。您可以只使用上面的 RegisterDependencies 但注册您的存根或模拟而不是生产实现。
那么你的测试 class(我使用的是 xUnit)将看起来像这样,但不要担心构造函数中的 AutoMapper 部分:
public class NewsControllerTests
{
private IContainer _testContainer;
private NewsController _controller;
public NewsControllerTests()
{
Mapper.Initialize(config =>
{
config.CreateMap<News, NewsDto>();
config.CreateMap<NewsDto, News>();
});
_testContainer = RegisterTestDependencies.testContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
new HttpConfiguration(), throwIfUnresolved: type => type.IsController());
RegisterTestDependencies.InitializeTestContainer(_testContainer);
}
[Fact]
void GetNews_WithoutId_ReturnsAllNewsItems()
{
//Arrange
using (var scope = _testContainer.OpenScope(Reuse.WebRequestScopeName))
{
_controller = scope.Resolve<NewsController>();
//Act
var getNewsResult = _controller.GetNews() as OkNegotiatedContentResult<List<NewsDto>>;
//Assert
getNewsResult.Content.Should().AllBeOfType<NewsDto>();
}
}
}
我一直在通读文档,对于如何实现这一点我有点困惑。我有一个名为 NewsController 的 WebAPI 控制器,我将在下面列出。它有一个构造函数,目前需要三个依赖项。我为 DryIoc 创建了一个静态 class,这样我就可以在整个项目中全局使用它,并且它会在启动时进行初始化。
我想做的是在我的 DryIoc class 中注册控制器及其依赖项,然后以某种方式在 NewsController 控制器中解决它。我想这样做的原因是,当我开始测试时,我可以简单地让测试项目更改已注册控制器的范围并使用存根或模拟实现。
RegisterDependencies
public static class RegisterDependencies
{
public static Container container;
public static void Initialize()
{
container = new Container(rules => rules
.WithDefaultReuseInsteadOfTransient(Reuse.InWebRequest)
.WithoutThrowOnRegisteringDisposableTransient()
.WithoutImplicitCheckForReuseMatchingScope());
container.Register<INewsManager, NewsManager>();
container.Register<IGetNews, NewsManager>();
container.Register<IAddNews, NewsManager>();
container.Register<ILoggingService, Logger>();
container.Register<NewsController>(made: Made.Of(() => new NewsController
(Arg.Of<ILoggingService>(), Arg.Of<IGetNews>(), Arg.Of<IAddNews>(),
Arg.Of<INewsManager>())));
}
}
NewsController(无论如何都是一部分)
public class NewsController : ApiController
{
private INewsManager _nm;
private IGetNews _getNews;
private IAddNews _addNews;
private ILoggingService _log;
public NewsController(ILoggingService logger, IGetNews getNews,
IAddNews addNews, INewsManager newsManager)
{
_getNews = getNews;
_addNews = addNews;
_log = logger;
_nm = newsManager;
}
[HttpGet]
public IHttpActionResult GetNews()
{
var newsItems = _getNews.GetNews();
if (newsItems == null || newsItems.Count() <= 0)
{
_log.Error("News Items couldn't be loaded.");
return NotFound();
}
return Ok(Mapper.Map<List<NewsDto>>(newsItems));
}
更新:
到目前为止,我通过 WebApiConfig 进行了 "automatic" 依赖解析,但我不确定如何从测试项目访问它,以便我可以用我的存根替换真实的实现。
WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RegisterDependencies.controllerContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
config, throwIfUnresolved: type => type.IsController());
RegisterDependencies.InitializeControllerContainer(RegisterDependencies.controllerContainer);
}
RegisterDependencies
public static class RegisterDependencies
{
public static IContainer controllerContainer;
public static void InitializeControllerContainer(IContainer ControllerContainer)
{
ControllerContainer.RegisterMany<ILoggingService, Logger>(setup: Setup.With(allowDisposableTransient: true));
ControllerContainer.RegisterMany<INews, NewsManager>(setup: Setup.With(allowDisposableTransient: true));
}
NewsController
public class NewsController : ApiController
{
private INews _news;
private ILoggingService _log;
public NewsController(INews news, ILoggingService logger)
{
_news = news;
_log = logger;
}
好的,这就是我的想法。
在上面 UPDATE: 下的问题中,您将看到使用 WebAPIConfig 自动注册和解析依赖项的代码。您需要使用 NuGet 和 DryIoc 中的 DryIoc.WebAPI 包。
为了能够替换测试实现,您需要为 test/mock 依赖项创建一个 class。您可以只使用上面的 RegisterDependencies 但注册您的存根或模拟而不是生产实现。
那么你的测试 class(我使用的是 xUnit)将看起来像这样,但不要担心构造函数中的 AutoMapper 部分:
public class NewsControllerTests
{
private IContainer _testContainer;
private NewsController _controller;
public NewsControllerTests()
{
Mapper.Initialize(config =>
{
config.CreateMap<News, NewsDto>();
config.CreateMap<NewsDto, News>();
});
_testContainer = RegisterTestDependencies.testContainer = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)).WithWebApi(
new HttpConfiguration(), throwIfUnresolved: type => type.IsController());
RegisterTestDependencies.InitializeTestContainer(_testContainer);
}
[Fact]
void GetNews_WithoutId_ReturnsAllNewsItems()
{
//Arrange
using (var scope = _testContainer.OpenScope(Reuse.WebRequestScopeName))
{
_controller = scope.Resolve<NewsController>();
//Act
var getNewsResult = _controller.GetNews() as OkNegotiatedContentResult<List<NewsDto>>;
//Assert
getNewsResult.Content.Should().AllBeOfType<NewsDto>();
}
}
}