【问题标题】:Ninject bind generic interfaceNinject 绑定泛型接口
【发布时间】:2017-10-12 22:41:26
【问题描述】:

这是对Generic Interface dependency injection into factory的跟进

答案是正确的,但我过于简化了代码。因为接口上没有 out 参数,所以您不能将 TOrderRequest 作为 create 方法中的输入参数。并且在界面上使用 Out 和 In 绑定将无法再次工作。

那么如何将它与 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
    {

    }
}

【问题讨论】:

    标签: c# dependency-injection ninject


    【解决方案1】:

    这就是我在您之前的问题“Create 的空实现和 this.createOrders.First() 有点混淆您想要实现的目标”中的评论点

    无论如何,这里的东西可能符合您的需求。它主要依赖于ICreateOrders中的CanHandle方法

    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
        {
    
        }
    }
    

    【讨论】:

    • 太棒了。谢谢@jbl。
    【解决方案2】:

    由于HorseOrderCreator 是一个封闭 泛型类型,将它绑定到一个开放 泛型typeof(ICreateOrders&lt;,&gt;) 确实没有意义。毕竟,HorseOrderCreator永远不可能是ICreateOrders&lt;Foo, Bar&gt;

    另外,无论是什么 DI 容器,您当前的代码都将永远无法工作。 试试这个:

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

    结果:

    System.InvalidCastException 无法将“HorseOrderCreator”类型的对象转换为“ICreateOrders`2[NinjectPlayGround.IOrderRequest,NinjectPlayGround.IOrderResponse]”类型的对象。

    因此,鉴于当前的接口约束,您的工厂将永远能够返回HorseOrderCreator

    所以这是一个设计问题。不是DI问题。并且该设计还可能包括界面的消费者,而这些消费者并未在问题中提供。所以我建议包括它们。

    【讨论】:

    • 感谢您的回答。我明白你的意思,但是这样做时仍然不会注入工厂。
    • @MacGyver 是的,抱歉,相应地更新了我的答案。