Ninject bind generic interface

2019-07-31 11:51发布

This is a follow up to Generic Interface dependency injection into factory

The answer is correct, but I oversimplified the code. Because with out parameter on the interface you can't have the TOrderRequest as an input parm in the create method. And with Out and In on the interface the binding wont work again.

So how do you bind this with Ninject?

using System;
using Ninject;
using System.Collections.Generic;
using System.Linq;

namespace NinjectPlayGround
{
    class Program
    {
        static void Main(string[] args)
        {
            var kernel = new StandardKernel();

            //How to bind this?
            kernel.Bind(typeof(ICreateOrders<,>)).To<HorseOrderCreator>();
            //kernel.Bind<ICreateOrders<IOrderRequest, IOrderResponse>>().To(typeof(OrderCreator));

            kernel.Bind<IOrderCreatorFactory>().To<OrderCreatorFactory>();

            var factory = kernel.Get<IOrderCreatorFactory>();

            var orderCreator = factory.GetOrderCreator(new OrderRequest());
            var create = orderCreator.Create(new OrderRequest());

        }
    }
    public class OrderRequest : IOrderRequest
    {

    }
    public class OrderResponse : IOrderResponse
    {

    }
    public class HorseOrderRequest : IOrderRequest
    {

    }
    public class HorseOrderResponse : IOrderResponse
    {
        public string HorseName { get; set; }
    }
    public class HorseOrderCreator : ICreateOrders<HorseOrderRequest, HorseOrderResponse>
    {        
        public HorseOrderResponse Create(HorseOrderRequest orderRequest)
        {
            return new HorseOrderResponse() { HorseName = "Fred" };
        }
    }
    public class OrderCreator : ICreateOrders<OrderRequest, OrderResponse>
    {        

        public OrderResponse Create(OrderRequest orderRequest)
        {
            throw new NotImplementedException();
        }
    }
    public class OrderCreatorFactory : IOrderCreatorFactory
    {
        private readonly IEnumerable<ICreateOrders<IOrderRequest, IOrderResponse>> createOrders;
        public OrderCreatorFactory(IEnumerable<ICreateOrders<IOrderRequest, IOrderResponse>> createOrders)
        {
            this.createOrders = createOrders;
        }

        public ICreateOrders<IOrderRequest, IOrderResponse> GetOrderCreator(IOrderRequest orderRequest)
        {
            //Based on orderRequest i find the implementation i need.
        }
    }
    public interface ICreateOrders<TOrderRequest, TOrderResponse> where TOrderRequest : IOrderRequest where TOrderResponse : IOrderResponse
    {
        TOrderResponse Create(TOrderRequest orderRequest);
    }
    public interface IOrderCreatorFactory
    {
        ICreateOrders<IOrderRequest, IOrderResponse> GetOrderCreator(IOrderRequest orderRequest);
    }
    public interface IOrderRequest
    {

    }
    public interface IOrderResponse
    {

    }
}

2条回答
趁早两清
2楼-- · 2019-07-31 12:16

Since HorseOrderCreator is a closed generic type, it really doesn't make sense to bind it to an open generic typeof(ICreateOrders<,>). After all, HorseOrderCreator can never be a ICreateOrders<Foo, Bar>!

Also, no matter what DI container, you're current code will never work. Try this:

ICreateOrders<IOrderRequest, IOrderResponse> createOrders =
    (ICreateOrders<IOrderRequest, IOrderResponse>)new HorseOrderCreator();

Results in:

System.InvalidCastException Unable to cast object of type 'HorseOrderCreator' to type 'ICreateOrders`2[NinjectPlayGround.IOrderRequest,NinjectPlayGround.IOrderResponse]'.

So your factory will never be able to return a HorseOrderCreator given the current interface constraints.

So this is a design problem. Not a DI problem. And that design might also include the consumers of the interface, which are not provided in the question. So I suggest to include them.

查看更多
爷的心禁止访问
3楼-- · 2019-07-31 12:29

That was the point of my comment in your previous question "The empty implementation of Create and the this.createOrders.First() somewhate obfuscates what you want to achieve"

Anyway here is something which might match your needs. It mostly relies on the CanHandle method in ICreateOrders

using System;
using Ninject;
using System.Collections.Generic;
using System.Linq;

namespace NinjectPlayGround
{   
    class Program
    {
        static void Main(string[] args)
        {
            var kernel = new StandardKernel();

            //How to bind this?
            kernel.Bind<ICreateOrders<IOrderRequest, IOrderResponse>>().To(typeof(OrderCreator));
            kernel.Bind<ICreateOrders<IOrderRequest, IOrderResponse>>().To(typeof(HorseOrderCreator));

            kernel.Bind<IOrderCreatorFactory>().To<OrderCreatorFactory>();

            var factory = kernel.Get<IOrderCreatorFactory>();

            var orderCreator = factory.GetOrderCreator(new OrderRequest());
            var orderResponse = orderCreator.Create(new OrderRequest());    
            if (!(orderResponse is OrderResponse)) throw new InvalidCastException();

            var horseOrderCreator = factory.GetOrderCreator(new HorseOrderRequest());
            var horseResponse = horseOrderCreator.Create(new HorseOrderRequest());    
            if (!(horseResponse is HorseOrderResponse)) throw new InvalidCastException();

            Console.WriteLine("All resolutions successfull");
            Console.ReadLine();

        }
    }
    public class OrderRequest : IOrderRequest
    {

    }
    public class OrderResponse : IOrderResponse
    {

    }
    public class HorseOrderRequest : IOrderRequest
    {

    }
    public class HorseOrderResponse : IOrderResponse
    {
        public string HorseName { get; set; }
    }

    public abstract class BaseOrderCreator<TOrderRequest, TOrderResponse> : ICreateOrders<IOrderRequest, IOrderResponse> where TOrderRequest : IOrderRequest where TOrderResponse : IOrderResponse
    {
        public bool CanHandle(IOrderRequest request)
        {
            return request is TOrderRequest;
        }

        public abstract TOrderResponse SpecificCreate(TOrderRequest orderRequest);

        public IOrderResponse Create(IOrderRequest orderRequest)
        {
            return this.SpecificCreate((TOrderRequest)orderRequest);
        }
    }

    public class HorseOrderCreator : BaseOrderCreator<HorseOrderRequest, HorseOrderResponse>
    {
        public override HorseOrderResponse SpecificCreate(HorseOrderRequest orderRequest)
        {
            return new HorseOrderResponse() { HorseName = "Fred" };
        }
    }
    public class OrderCreator : BaseOrderCreator<OrderRequest, OrderResponse>
    {
        public override OrderResponse SpecificCreate(OrderRequest orderRequest)
        {
            return new OrderResponse();
        }
    }
    public class OrderCreatorFactory : IOrderCreatorFactory
    {
        private readonly IEnumerable<ICreateOrders<IOrderRequest, IOrderResponse>> createOrders;
        public OrderCreatorFactory(IEnumerable<ICreateOrders<IOrderRequest, IOrderResponse>> createOrders)
        {
            this.createOrders = createOrders;
        }

        public ICreateOrders<IOrderRequest, IOrderResponse> GetOrderCreator(IOrderRequest orderRequest)
        {
            return createOrders.FirstOrDefault(co => co.CanHandle(orderRequest));
        }
    }
    public interface ICreateOrders<in TOrderRequest, out TOrderResponse> where TOrderRequest : IOrderRequest where TOrderResponse : IOrderResponse
    {
        bool CanHandle(IOrderRequest request);

        TOrderResponse Create(TOrderRequest orderRequest);
    }
    public interface IOrderCreatorFactory
    {
        ICreateOrders<IOrderRequest, IOrderResponse> GetOrderCreator(IOrderRequest orderRequest);
    }
    public interface IOrderRequest
    {

    }
    public interface IOrderResponse
    {

    }
}
查看更多
登录 后发表回答