Search code examples
c#asp.net-mvcdependency-injectionninjectninject.web.mvc

How pass parameters of controller's action to Ninject concrete type?


I have ProductController.cs

    namespace AmazonProductAdvertisingAPI.WebUI.Controllers
    {
        public class ProductController : Controller
        {

            public ProductController(IProductCollection productCollection)
            {
                _productCollection = productCollection;
            }

            public static string Title
            {
                get
                {
                    return _title;
                }
                set
                {
                    _title = value;
                }
            }
            public static int PageNumber
            {
                get
                {
                    return _pageNumber;
                }
                set
                {
                    _pageNumber = value;
                }
            }
            public static int ItemsPerPage
            {
                get
                {
                    return _itemsPerPage;
                }
                set
                {
                    _itemsPerPage = value;
                }
            }
            // GET: Product
            public ActionResult List(int page = 1, string search = null)
            {

                ProductListViewModel model = new ProductListViewModel
                {
                    Products = _productCollection.Products
                                                 .OrderBy(product => product.Title)
                                                 .Skip((page - 1) * pageSize)
                                                 .Take(pageSize),
                    PagingInfo = new PagingInfo
                    {
                        CurrentPage = page,
                        ItemsPerPage = pageSize,
                        TotalItems = _productCollection.Products.Count()
                    }
                };
                return View(model);
            }
        }
    }

NinjectDependencyResolver.cs

namespace AmazonProductAdvertisingAPI.WebUI.Infrastructure
{
    public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver(IKernel kernelParam)
        {
            kernel = kernelParam;
            AddBindings();
        }
        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        private void AddBindings()
            {
                // Create dependency here

                kernel.Bind<IProductCollection>().To<AmazonProductCollection>()
                                                 .WhenInjectedInto<ProductController>()
                                                 .WithConstructorArgument("title", ProductController.Title)
                                                 .WithConstructorArgument("pageNumber", ProductController.PageNumber)
                                                 .WithConstructorArgument("itemsPerPage", ProductController.ItemsPerPage);
            }
        }
    }

AmazonProductCollection class have constructor:

public AmazonProductCollection(string title, int pageNumber, int itemsPerPage)

I want that AmazonProductCollection get own parameters from action List parameters from Product controller, because some of this it gets when user fill TextBoxt and click to button "Search" in html-view form. For example I want use parameter string "search" from action List and transfer to AmazonProductCollection as constructor parameter "title".

I read this post: How to pass parameters to a transient object created by Ninject?, but I don't understand how possible to create same things in my situation.

Can somebody help me with Ninject?


Solution

  • One solution is to use a factory.

    The factory interface would look something like this:

    public interface IAmazonProductCollectionFactory
    {
        AmazonProductCollection Create(string title, int pageNumber, int itemsPerPage);
    }
    

    Such interface would live in the same project where the controller is (the MVC project).

    The implementation of such factory would look like this:

    public class AmazonProductCollectionFactory : IAmazonProductCollectionFactory
    {
        private readonly IResolutionRoot m_ResolutionRoot;
    
        public AmazonProductCollectionFactory (IResolutionRoot resolution_root)
        {
            m_ResolutionRoot = resolution_root;
        }
    
        public AmazonProductCollection Create(string title, int pageNumber, int itemsPerPage)
        {
            return resolution_root.Get<AmazonProductCollection>(
                new ConstructorArgument("title", title),
                new ConstructorArgument("pageNumber", pageNumber),
                new ConstructorArgument("itemsPerPage", pageNumber));
        }
    }
    

    The AmazonProductCollectionFactory would live inside the Composition Root project. In your case, this is probably the same MVC project. Please note that having such factory that depends on IResolutionRoot any place except for the composition root is an example of service location which is considered an anti-pattern.

    Now, instead of IProductCollection, you need to inject IAmazonProductCollectionFactory into the constructor of the ProductController and let the List action use it to create an AmazonProductCollection instance like this:

    var productCollection = factory.Create(...);
    

    where factory is the name of the instance variable that you used to store the injected IAmazonProductCollectionFactory.

    Don't forget to register IAmazonProductCollectionFactory with the Ninject container.

    Please note that you should consider creating a service that matches the requirements of the List action. For example, you can wrap the whole search logic inside a service that knows how to do the search and let such service worry about creating the AmazonProductCollection via the abstract factory. And then, instead of injecting the factory into the controller, you would inject the service into the controller.