如何让Ninject将特定的SerialPort实例注入另一个类的特定实例?

时间:2016-03-04 22:03:56

标签: dependency-injection serial-port driver ninject windows-10-universal

[这适用于Raspberry Pi 2上的Windows 10 IoT UWP应用]:

具体来说,我尝试创建两个串行端口并将每个串行端口连接到具有串行连接的设备的设备驱动程序(我有两个相同的设备和一个串行端口与每个串口连接)。我有一个类(称为DeviceDriver),它为这种类型的设备实现了一个IDeviceDriver接口。我的硬件配置还包括一个具有多个串行端口的外部芯片。我有一个SerialPort类,它们实现了一个ISerialPort接口。

我需要两个DeviceDriver实例,因为我有两个设备,还有两个SerialPort实例 - 每个设备一个。我可以让Ninject创建一个serialport并将ISerialPort对象传递给DeviceDriver构造函数。我被困的地方是我想要两个DeviceDriver对象;一个链接到SerialPort(COM1),另一个链接到单独的SerialPort(COM2)。 Ninject的示例显示您可以将多个不同的类绑定到一个实例(Shuriken和Sword都可以绑定到IWeapon),但我不知道如何将COM1 serialport和COM2 serialport绑定到ISerialPort - 它给了我编译错误。那么如何创建同一个SerialPort类的两个实例(使用不同的构造函数参数,一个是COM1,另一个是COM2,我知道如何指定构造函数参数),然后告诉Ninject哪个SerialPort传递给两个实例DeviceDriver类,其中一个需要COM1而一个需要COM2?

我的DeviceDriver基本上是这样的:

public class DeviceDriver :IDeviceDriver
{
    ISerialPort m_localPort;

    public DeviceDriver(ISerialPort port)
    {
        m_localPort = port;
    }

    // Other stuff
    // ...
}

任何人都有任何想法我怎么能这样做?以下链接是我发现的唯一链接,但他们谈论的是Unity和XML配置文件,对于我尝试做的事情似乎过于复杂。

Initialising configurable objects with dependency injection container

谢谢!

2 个答案:

答案 0 :(得分:2)

假设我们有以下实现:

public class SerialPortAddress
{
    public SerialPortAddress(string address)
    {
        this.Address = address;
    }

    public string Address { get; }
}

public interface ISerialPort
{
    SerialPortAddress Address { get; }
}

public class SerialPort : ISerialPort
{
    public SerialPort(SerialPortAddress address)
    {
        this.Address = address;
    }

    public SerialPortAddress Address { get; }
}

public interface IDeviceDriver
{
    ISerialPort SerialPort { get; }
}

public class DeviceDriver : IDeviceDriver
{
    public DeviceDriver(ISerialPort serialPort)
    {
        SerialPort = serialPort;
    }

    public ISerialPort SerialPort { get; }
}

多喷射

然后,我们可以按如下方式创建绑定,并检索IDeviceDrivers及其串口的列表,如下所示:

public class Test
{
    [Fact]
    public void Bla()
    {
        var com1 = new SerialPortAddress("COM1");
        var com2 = new SerialPortAddress("COM2");

        var kernel = new StandardKernel();

        kernel.Bind<ISerialPort>().To<SerialPort>();
        kernel.Bind<IDeviceDriver>().To<DeviceDriver>()
            .WithParameter(new TypeMatchingConstructorArgument(
                typeof(SerialPortAddress),
                (ctx, target) => com1, true));
        kernel.Bind<IDeviceDriver>().To<DeviceDriver>()
            .WithParameter(new TypeMatchingConstructorArgument(
                typeof(SerialPortAddress),
                (ctx, target) => com2, true));

        var deviceDrivers = kernel.Get<List<IDeviceDriver>>();

        deviceDrivers.Should().HaveCount(2)
            .And.Contain(x => x.SerialPort.Address == com1)
            .And.Contain(x => x.SerialPort.Address == com2);
    }
}

另见Multi Injection

命名绑定

或者,如果您需要知道哪个IDeviceDrivers,您还可以使用命名绑定:

[Fact]
public void NamedBindings()
{
    const string DeviceDriver1 = "DeviceDriver1";
    const string DeviceDriver2 = "DeviceDriver2";

    var com1 = new SerialPortAddress("COM1");
    var com2 = new SerialPortAddress("COM2");

    var kernel = new StandardKernel();

    kernel.Bind<ISerialPort>().To<SerialPort>();
    kernel.Bind<IDeviceDriver>().To<DeviceDriver>()
        .Named(DeviceDriver1)
        .WithParameter(new TypeMatchingConstructorArgument(
            typeof(SerialPortAddress), 
            (ctx, target) => com1, true));
    kernel.Bind<IDeviceDriver>().To<DeviceDriver>()
        .Named(DeviceDriver2)
        .WithParameter(new TypeMatchingConstructorArgument(
            typeof(SerialPortAddress),
            (ctx, target) => com2, true));

    kernel.Get<IDeviceDriver>(DeviceDriver1).SerialPort.Address.Should().Be(com1);
    kernel.Get<IDeviceDriver>(DeviceDriver2).SerialPort.Address.Should().Be(com2);
}

最后,您还可以按工厂创建组件,这需要以工厂界面开头:

public interface IDeviceDriverFactory
{
    IDeviceDriver Create(SerialPortAddress address);
}

使用Ninject.Extensions.Factory我们现在可以执行以下操作:

[Fact]
public void Factory()
{
    var com1 = new SerialPortAddress("COM1");
    var com2 = new SerialPortAddress("COM2");

    var kernel = new StandardKernel();

    kernel.Bind<ISerialPort>().To<SerialPort>();
    kernel.Bind<IDeviceDriver>().To<DeviceDriver>();
    kernel.Bind<IDeviceDriverFactory>()
          .ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());

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

    factory.Create(com1).SerialPort.Address.Should().Be(com1);
    factory.Create(com2).SerialPort.Address.Should().Be(com2);
}

编辑:Ninject.Extension.Factory可能无法在raspberry pi上运行。 如果是这种情况,您可能需要自己实施工厂:

public class DeviceDriverFactory : IDeviceDriverFactory
{
    private readonly IResolutionRoot resolutionRoot;

    public DeviceDriverFactory(IResolutionRoot resolutionRoot)
    {
        this.resolutionRoot = resolutionRoot;
    }

    public IDeviceDriver Create(SerialPortAddress address)
    {
        var serialPortAddressParameter = new TypeMatchingConstructorArgument(
            typeof(SerialPortAddress),
            (ctx, t) => address)
        this.resolutionRoot.Get<IDeviceDriver>(serialPortAddressParameter);
    }
}

Bind<IDeviceDriverFactory>().To<DeviceDriverFactory>();

答案 1 :(得分:0)

我不熟悉Ninject或Unity,但Castle Windsor有一种名为Pooled的生活方式,它会创建指定数量的实例,然后将这些实例返回到实例池后#&# 39;已被释放。使用这种生活方式时,Windsor将根据指定的限制创建尽可能多的对象,然后循环实例(如果您从IRecyclable派生并实现了Recycle()方法)或正常处理它。

您可以使用提供正确构造函数参数的简单工厂方法创建组件,然后在将它们返回池时将正确配置它们。

编辑: 如果你开始使用Ninject,那么我会通过将一个ISerialPortFactory注入到DeviceDriver的构造函数中并使用它来创建你的ISerialPort对象来解决这个问题。由于您的DeviceDriver类并不关心它使用的ISerialPort,因此工厂可用于管理您需要的实例。

你的工厂看起来像这样:

public interface ISerialPortFactory
{
    ISerialPort CreateNext();
}

public class SerialPortFactory : ISerialPortFactory
{        

    public ISerialPort CreateNext()
    {
        var serialPortConfiguration = GetNextConfiguration();
        return new SerialPort(serialPortConfiguration);
    }

    private GetNextConfiguration()
    {
        // you could manage some kind of internal registry of COMx configurations here
    }

}

您的客户端DeviceDriver类看起来像这样:

public class DeviceDriver : IDeviceDriver
{
    public DeviceDriver(ISerialPortFactory factory)
    {
        m_localPort = factory.CreateNext();
    }
}

抽象工厂方法是一种苛刻的方式来获得你想要的东西,但它是一种确定的方式来获得你需要的东西,因为你可以完全控制它。它的主要用例是解决依赖关系,在运行时,您不必知道所需的确切实现。