在两个类具有相同的基础接口依赖项,但每个类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与字符串参数相比更具体。从我的立场看来,我要么只是管理多个参数名称的情况,要么切换到自定义类型作为我的参数而不是字符串。
可以使用“类型匹配”或“类型化”参数来代替“命名参数”。IInstanceProvider
可以将工厂交换为另一个工厂:
kernel.Bind<IThingFactory>()
.ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
笔记:
IInstanceProvider
也将使参数进一步“下游”可用(它“继承”参数)string
非常冗长,因此您可能希望选择将其包装为其他类型class ConnectionInfo
。假设我们创建了自己的FileType
类型,而不是仅仅使用string
以下类型:
public class FileCode
{
public FileCode(string value)
{
Value = value;
}
public string Value { get; private set; }
}
(也许您想将其替换为enum
?)
由于您的要求更加复杂,因此我们将不得不进行一些更改。我们将创建自己IConstructorArgument
的When
脚本,以轻松地将其与-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
但是除非我们打算使用FileCodeParameter
si创建许多工厂,否则认为这没有道理,因此让我们保持简单:
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] 删除。
我来说两句