如何使用DryIoc解析WebAPI控制器的依赖关系

时间:2017-08-16 19:54:26

标签: c# unit-testing asp.net-web-api inversion-of-control dryioc

我一直在阅读文档,我对如何实现这一点感到困惑。我有一个名为NewsController的WebAPI控制器,我将在下面列出。它有一个构造函数,目前有三个依赖项。我为DryIoc创建了一个静态类,所以我可以在整个项目中全局使用它,并在启动时初始化。

我想要做的是在我的DryIoc类中注册控制器及其依赖项,然后以某种方式在NewsController控制器中解析它。我想这样做的原因是,当我开始测试时,我可以简单地让测试项目改变已注册控制器的范围并使用存根或模拟实现。

RegisterDependencies

 var force = d3.layout.force()
    .nodes(data)
    .size([+width(), +height()])
    .gravity(.08)
    .charge(-10)
    .on("tick", tick)
    .on("end", function(){
      chart.dispatchEndDrawing()
    })
    .start();

var g = selection
    .attr("width", width)
    .attr("height", height);


var link = g.selectAll("line")
    .data(data.filter(function (d){ return d.type == "link"; }))
    .enter().append("line")
        .style("stroke-width", function(d) { return d.interest; })
        .style("stroke", "grey")
        .call(force.drag); 

var node = g.selectAll("circle")
    .data(data.filter(function (d){ return d.type == "node"; }))
    .enter().append("circle")
    .attr("r", 5)
    .style("fill", "blue")
        .call(force.drag);


function tick(e) {
    node
      .attr("cx", function(d) { return d.x = Math.max(radius(), Math.min(width() - radius(), d.x)); })
      .attr("cy", function(d) { return d.y = Math.max(radius(), Math.min(height()-10 - radius(), d.y)); });


    link
      .attr("x1", function(d) { return d3.selectAll('circle').filter(function (k) { return d.source === k.node; }).attr('cx');})
      .attr("y1", function(d) { return d3.selectAll('circle').filter(function (k) { return d.source === k.node; }).attr('cy'); })   
      .attr("x2", function(d) { return d3.selectAll('circle').filter(function (k) { return d.target === k.node; }).attr('cx'); })
      .attr("y2", function(d) { return d3.selectAll('circle').filter(function (k) { return d.target === k.node; }).attr('cy'); });

         chart.dispatchStartDrawing()

}

})

NewsController(无论如何都是其中的一部分)

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>())));
    }
}

更新

到目前为止,我通过WebApiConfig进行了“自动”依赖性解析,但我不确定如何从测试项目中访问它,因此我可以用我的存根替换实际的实现。

WebApiConfig

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));
    }

RegisterDependencies

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);

      }

NewsController

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));
        }

1 个答案:

答案 0 :(得分:1)

这就是我想出来的。

UPDATE:下面的问题中,您将看到使用WebAPIConfig自动注册和解析依赖关系的代码。您需要使用NuGet的DryIoc.WebAPI包以及DryIoc。

为了能够交换测试实现,您需要为测试/模拟依赖项创建一个类。您可以使用上面的 RegisterDependencies ,但注册您的存根或模拟而不是生产实现。

然后你的测试类(我正在使用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>();

        }


    }
}