为同一个构造函数参数使用不同名称的2+个依赖类的Ninject上下文绑定

布雷特·罗西尔(Brett Rossier)

在两个类具有相同的基础接口依赖项,但每个类ctor的参数命名不同的情况下,很难弄清楚如何管理上下文绑定。下面的伪代码演示了我的情况:

    interface IThing { }
    public class Thing1 : IThing { public Thing1(string fileCode) { } }
    public class Thing2 : IThing { public Thing2(string fileCode) { } }
    interface IThingFactory { IThing CreateThing(string fileCode); }

    interface IDependentThing { }
    public class A : IDependentThing { public A(string fileCode, IThingFactory thingFactory) { } }
    public class B : IDependentThing { public B(string fileCd, IThingFactory thingFactory) { } } //How to handle binding for this dependent?
    interface IDependentThingFactory { IDependentThing CreateDependentThing(string fileCode); }

    //...

    public override void Load()
    {
        Bind<IThing>().ToMethod(ctx =>
        {
            var fileCode = ctx.Parameters.First(p => p.Name == "fileCode").GetValue(ctx, null) as string;
            IThing thing = null;

            if (fileCode == "FileType1")
            {
                Bind<Thing1>().ToSelf().WithConstructorArgument("fileCode", fileCode);
                thing = Kernel.Get<Thing1>();
            }
            else if (fileCode == "FileType2")
            {
                Bind<Thing2>().ToSelf().WithConstructorArgument("fileCode", fileCode);
                thing = Kernel.Get<Thing2>();
            }
            return thing;
        });

        Bind<IThingFactory>().ToFactory();
        Bind<IDependentThingFactory>().ToFactory();
    }

//Later...
using (TextReader tr = new StreamReader(path))
{
    string firstLine = tr.ReadLine();

    if (firstLine.Substring(838, 1) == ".")
    {
        fileCode = "FileType1";
    }
    else if (firstLine.Substring(883, 1) == ".")
    {
        fileCode = "FileType2";
    }

    //won't work for creating B
    Kernel.Get<IDependentThing>(new ConstructorArgument("fileCode", fileCode));

    //or maybe...

    //seems to eliminate my problem by allowing me to handle variations
    //in parameter names  from within A and B's ctors, but looks like it
    //requires injecting factories along the chain (see A & B ctor arguments).
    dependentThingFactory.CreateDependentThing(fileCode) 
};

fileCode是根据对本地文件的一些分析计算得出的。确定文件类型后,我希望Ninject交出适当的对象以处理该文件

B由于定义的现有绑定需要使用其他名称的构造函数参数,我将如何处理该绑定通常有更好的方法吗?

我想我可以使用p.Name == "fileCode" || p.Name == "fileCd",但是我无法摆脱做错事情的感觉(感觉很乱)。另外,我对按名称提取参数并不感到兴奋,并且我考虑过可能创建一个自定义类型,该类型将使Ninject与字符串参数相比更具体。从我的立场看来,我要么只是管理多个参数名称的情况,要么切换到自定义类型作为我的参数而不是字符串。

BatteryBackupUnit

使参数注入更加重构安全,并使它们可用于整个分辨率环境

可以使用“类型匹配”或“类型化”参数来代替“命名参数”。IInstanceProvider可以将工厂交换为另一个工厂

kernel.Bind<IThingFactory>()
      .ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());

笔记:

  • IInstanceProvider也将使参数进一步“下游”可用(它“继承”参数)
  • astring非常冗长,因此您可能希望选择将其包装为其他类型class ConnectionInfo

上下文绑定结合参数注入

假设我们创建了自己的FileType类型,而不是仅仅使用string以下类型

public class FileCode
{
    public FileCode(string value)
    {
        Value = value;
    }

    public string Value { get; private set; }
}

(也许您想将其替换为enum?)

由于您的要求更加复杂,因此我们将不得不进行一些更改。我们将创建自己IConstructorArgumentWhen脚本,以轻松地将其与-contextual绑定进行匹配,并基于类型匹配(如上所述)注入其值:

internal class FileCodeParameter : IConstructorArgument
{
    private readonly FileCode fileCode;

    public FileCodeParameter(FileCode fileCode)
    {
        this.fileCode = fileCode;
    }

    public string Name { get { return "File Code Parameter"; } }

    public bool ShouldInherit { get { return true; } }

    public FileCode FileCode  { get { return this.fileCode; } }

    public bool Equals(IParameter other)
    {
        var otherFileCodeParameter = other as FileCodeParameter;
        if (otherFileCodeParameter == null)
        {
            return false;
        }

        return otherFileCodeParameter.fileCode == this.fileCode;
    }

    public object GetValue(IContext context, ITarget target)
    {
        return this.fileCode;
    }

    public bool AppliesToTarget(IContext context, ITarget target)
    {
        return target.Type == typeof(FileCode);
    }
}

现在,让我创建一些示例代码,以便以后可以验证它是否有效:

public interface IThing
{
    FileCode FileCode { get; }
}

public abstract class Thing : IThing
{
    protected Thing(FileCode fileCode)
    {
        FileCode = fileCode;
    }

    public FileCode FileCode { get; private set; }
}

public class ThingFoo : Thing
{
    public ThingFoo(FileCode fileCode) : base(fileCode) { }
}

public class ThingBar : Thing
{
    public ThingBar(FileCode fileCode) : base(fileCode) { }
}

public interface IOtherThing
{
    FileCode FileCode { get; }
}

public abstract class OtherThing : IOtherThing
{
    protected OtherThing(FileCode fileCode)
    {
        FileCode = fileCode;
    }

    public FileCode FileCode { get; private set; }
}

public class OtherThingFoo : OtherThing
{
    public OtherThingFoo(FileCode fileCode) : base(fileCode) { }
}

public class OtherThingBar : OtherThing
{
    public OtherThingBar(FileCode fileCode) : base(fileCode) { }
}

public class OtherThingWrapper
{
    public OtherThingWrapper(IOtherThing otherThing)
    {
        OtherThing = otherThing;
    }

    public IOtherThing OtherThing { get; private set; }
}

public class FileProcessor
{
    public FileProcessor(IThing thing, OtherThingWrapper otherThingWrapper)
    {
        Thing = thing;
        OtherThingWrapper = otherThingWrapper;
    }

    public IThing Thing { get; private set; }
    public OtherThingWrapper OtherThingWrapper { get; private set; }
}

少了什么东西?该工厂。我们可以将ToFactory绑定与custom一起使用,IInstanceProvider但是除非我们打算使用FileCodeParametersi创建许多工厂,否则认为这没有道理,因此让我们保持简单:

public interface IFileProcessorFactory
{
    FileProcessor Create(FileCode fileCode);
}

internal class FileProcessorFactory : IFileProcessorFactory
{
    private readonly IResolutionRoot resolutionRoot;

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

    public FileProcessor Create(FileCode fileCode)
    {
        return this.resolutionRoot.Get<FileProcessor>(new FileCodeParameter(fileCode));
    }
}

现在,让我们将它们放在一起:

public class Test
{
    [Fact]
    public void FactMethodName()
    {
        var fooFileCode = new FileCode("foo");
        var barFileCode = new FileCode("bar");

        var kernel = new StandardKernel();
        kernel
            .Bind<IFileProcessorFactory>()
            .To<FileProcessorFactory>();

        kernel
            .Bind<IThing>()
            .To<ThingFoo>()
            .WhenFileCode(fooFileCode);
        kernel
            .Bind<IThing>()
            .To<ThingBar>()
            .WhenFileCode(barFileCode);

        kernel
            .Bind<IOtherThing>()
            .To<OtherThingFoo>()
            .WhenFileCode(fooFileCode);
        kernel
            .Bind<IOtherThing>()
            .To<OtherThingBar>()
            .WhenFileCode(barFileCode);


        var fileProcessor = kernel.Get<IFileProcessorFactory>().Create(barFileCode);
        fileProcessor.Thing.Should().BeOfType<ThingBar>();
        fileProcessor.Thing.FileCode.Should().Be(barFileCode);
        fileProcessor.OtherThingWrapper.OtherThing.Should().BeOfType<OtherThingBar>();
        fileProcessor.OtherThingWrapper.OtherThing.FileCode.Should().Be(barFileCode);
    }
}

public static class BindingExtensionsForFileCodes
{
    public static IBindingInNamedWithOrOnSyntax<T> WhenFileCode<T>(
        this IBindingWhenSyntax<T> syntax,
        FileCode fileCode)
    {
        return syntax.When(req => req
            .Parameters
            .OfType<FileCodeParameter>()
            .Single()
            .FileCode.Value == fileCode.Value);
    }
}

而已!-FileCode既被注入又用于选择实现-由于参数是“继承的”,因此它也可以在对象树的更深层工作。

下面,仅供参考,所有代码可简化复制和粘贴:

using FluentAssertions;
using Ninject;
using Ninject.Activation;
using Ninject.Parameters;
using Ninject.Planning.Targets;
using Ninject.Syntax;
using System.Linq;
using Xunit;

namespace NinjectTest.ParameterContextual
{
    public class FileCode
    {
        public FileCode(string value)
        {
            Value = value;
        }

        public string Value { get; private set; }
    }

    public interface IThing
    {
        FileCode FileCode { get; }
    }

    public abstract class Thing : IThing
    {
        protected Thing(FileCode fileCode)
        {
            FileCode = fileCode;
        }

        public FileCode FileCode { get; private set; }
    }

    public class ThingFoo : Thing
    {
        public ThingFoo(FileCode fileCode) : base(fileCode) { }
    }

    public class ThingBar : Thing
    {
        public ThingBar(FileCode fileCode) : base(fileCode) { }
    }

    public interface IOtherThing
    {
        FileCode FileCode { get; }
    }

    public abstract class OtherThing : IOtherThing
    {
        protected OtherThing(FileCode fileCode)
        {
            FileCode = fileCode;
        }

        public FileCode FileCode { get; private set; }
    }

    public class OtherThingFoo : OtherThing
    {
        public OtherThingFoo(FileCode fileCode) : base(fileCode) { }
    }

    public class OtherThingBar : OtherThing
    {
        public OtherThingBar(FileCode fileCode) : base(fileCode) { }
    }

    public class OtherThingWrapper
    {
        public OtherThingWrapper(IOtherThing otherThing)
        {
            OtherThing = otherThing;
        }

        public IOtherThing OtherThing { get; private set; }
    }

    public class FileProcessor
    {
        public FileProcessor(IThing thing, OtherThingWrapper otherThingWrapper)
        {
            Thing = thing;
            OtherThingWrapper = otherThingWrapper;
        }

        public IThing Thing { get; private set; }
        public OtherThingWrapper OtherThingWrapper { get; private set; }
    }

    public interface IFileProcessorFactory
    {
        FileProcessor Create(FileCode fileCode);
    }

    internal class FileProcessorFactory : IFileProcessorFactory
    {
        private readonly IResolutionRoot resolutionRoot;

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

        public FileProcessor Create(FileCode fileCode)
        {
            return this.resolutionRoot.Get<FileProcessor>(new FileCodeParameter(fileCode));
        }
    }

    public class Test
    {
        [Fact]
        public void FactMethodName()
        {
            var fooFileCode = new FileCode("foo");
            var barFileCode = new FileCode("bar");

            var kernel = new StandardKernel();
            kernel
                .Bind<IFileProcessorFactory>()
                .To<FileProcessorFactory>();

            kernel
                .Bind<IThing>()
                .To<ThingFoo>()
                .WhenFileCode(fooFileCode);
            kernel
                .Bind<IThing>()
                .To<ThingBar>()
                .WhenFileCode(barFileCode);

            kernel
                .Bind<IOtherThing>()
                .To<OtherThingFoo>()
                .WhenFileCode(fooFileCode);
            kernel
                .Bind<IOtherThing>()
                .To<OtherThingBar>()
                .WhenFileCode(barFileCode);


            var fileProcessor = kernel.Get<IFileProcessorFactory>().Create(barFileCode);
            fileProcessor.Thing.Should().BeOfType<ThingBar>();
            fileProcessor.Thing.FileCode.Should().Be(barFileCode);
            fileProcessor.OtherThingWrapper.OtherThing.Should().BeOfType<OtherThingBar>();
            fileProcessor.OtherThingWrapper.OtherThing.FileCode.Should().Be(barFileCode);
        }
    }

    internal class FileCodeParameter : IConstructorArgument
    {
        private readonly FileCode fileCode;

        public FileCodeParameter(FileCode fileCode)
        {
            this.fileCode = fileCode;
        }

        public string Name { get { return "File Code Parameter"; } }

        public bool ShouldInherit { get { return true; } }

        public FileCode FileCode  { get { return this.fileCode; } }

        public bool Equals(IParameter other)
        {
            var otherFileCodeParameter = other as FileCodeParameter;
            if (otherFileCodeParameter == null)
            {
                return false;
            }

            return otherFileCodeParameter.fileCode == this.fileCode;
        }

        public object GetValue(IContext context, ITarget target)
        {
            return this.fileCode;
        }

        public bool AppliesToTarget(IContext context, ITarget target)
        {
            return target.Type == typeof(FileCode);
        }
    }

    public static class BindingExtensionsForFileCodes
    {
        public static IBindingInNamedWithOrOnSyntax<T> WhenFileCode<T>(
            this IBindingWhenSyntax<T> syntax,
            FileCode fileCode)
        {
            return syntax.When(req => req
                .Parameters
                .OfType<FileCodeParameter>()
                .Single()
                .FileCode.Value == fileCode.Value);
        }
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

为同一个构造函数参数使用不同名称的2+个依赖类的Ninject上下文绑定

来自分类Dev

使用NInject进行上下文绑定

来自分类Dev

使用依赖于上下文绑定的选择编写类型类

来自分类Dev

构造函数未将参数绑定到类上下文

来自分类Dev

当不在对象上下文中时,在同一个类中使用$ this

来自分类Dev

Ninject是否创建2个单独的上下文?

来自分类Dev

如何让 Simple Injector 为同一个接口使用不同的具体类,但使用不同的类

来自分类Dev

为什么在同一个类上使用不同的(shared_ptr和普通)指针构造函数会得到不同的结果?

来自分类Dev

WPF绑定同一控件的两个依赖项属性,但需要两个数据上下文

来自分类Dev

Scala中是否可以有两个或多个上下文绑定类

来自分类Dev

如何使用不同的 id 多次调用同一个函数

来自分类Dev

ANTLR4:如何在规则内为同一个标签设置不同的上下文?

来自分类Dev

Vue-同时使用具有多种配置和不同名称的同一个插件?

来自分类Dev

如何在 for 循环中使用多处理并行化对同一个函数的两次调用,并使用不同的参数?

来自分类Dev

将多个视图绑定到同一个数据上下文 (WPF-MVVM)

来自分类Dev

在同一个类中使用不同的函数时如何在Python中获取Checkbox的值

来自分类Dev

使用 Ninject 上下文绑定时如何检索属性和属性?

来自分类Dev

Javascript——如何使用同一个onkeydown事件在不同场合调用不同的函数

来自分类Dev

使用不同的 ENV 变量为同一个 Rails 应用程序提供服务

来自分类Dev

委托给一个更具体的上下文绑定(附加的隐式参数)

来自分类Dev

在同一个程序包中使用相同名称区分结构

来自分类Dev

在同一个javascript事件处理程序中调用不同的函数

来自分类Dev

对同一个班级应用不同的等于

来自分类Dev

如何使用 Knockoutjs “with” 上下文并仅更新一个绑定值?

来自分类Dev

Scala:在第二个构造函数中显式指定上下文绑定

来自分类Dev

Scala:在第二个构造函数中显式指定上下文绑定

来自分类Dev

如何从具有上下文的另一个类调用函数(android,java)

来自分类Dev

如何在同一个2D数组中使用两个构造函数?爪哇

来自分类Dev

在同一个甜甜圈图中使用不同的数据

Related 相关文章

  1. 1

    为同一个构造函数参数使用不同名称的2+个依赖类的Ninject上下文绑定

  2. 2

    使用NInject进行上下文绑定

  3. 3

    使用依赖于上下文绑定的选择编写类型类

  4. 4

    构造函数未将参数绑定到类上下文

  5. 5

    当不在对象上下文中时,在同一个类中使用$ this

  6. 6

    Ninject是否创建2个单独的上下文?

  7. 7

    如何让 Simple Injector 为同一个接口使用不同的具体类,但使用不同的类

  8. 8

    为什么在同一个类上使用不同的(shared_ptr和普通)指针构造函数会得到不同的结果?

  9. 9

    WPF绑定同一控件的两个依赖项属性,但需要两个数据上下文

  10. 10

    Scala中是否可以有两个或多个上下文绑定类

  11. 11

    如何使用不同的 id 多次调用同一个函数

  12. 12

    ANTLR4:如何在规则内为同一个标签设置不同的上下文?

  13. 13

    Vue-同时使用具有多种配置和不同名称的同一个插件?

  14. 14

    如何在 for 循环中使用多处理并行化对同一个函数的两次调用,并使用不同的参数?

  15. 15

    将多个视图绑定到同一个数据上下文 (WPF-MVVM)

  16. 16

    在同一个类中使用不同的函数时如何在Python中获取Checkbox的值

  17. 17

    使用 Ninject 上下文绑定时如何检索属性和属性?

  18. 18

    Javascript——如何使用同一个onkeydown事件在不同场合调用不同的函数

  19. 19

    使用不同的 ENV 变量为同一个 Rails 应用程序提供服务

  20. 20

    委托给一个更具体的上下文绑定(附加的隐式参数)

  21. 21

    在同一个程序包中使用相同名称区分结构

  22. 22

    在同一个javascript事件处理程序中调用不同的函数

  23. 23

    对同一个班级应用不同的等于

  24. 24

    如何使用 Knockoutjs “with” 上下文并仅更新一个绑定值?

  25. 25

    Scala:在第二个构造函数中显式指定上下文绑定

  26. 26

    Scala:在第二个构造函数中显式指定上下文绑定

  27. 27

    如何从具有上下文的另一个类调用函数(android,java)

  28. 28

    如何在同一个2D数组中使用两个构造函数?爪哇

  29. 29

    在同一个甜甜圈图中使用不同的数据

热门标签

归档