我对实现lambdas和表达式并不是很熟悉,但我在MVC中多次习惯这种语法,其中lambda正在识别对象的属性:
Html.Label(model => model.Foo)
在我的应用程序中,我使用Ninject条件绑定来提供Settings
类的实例,该实例是在请求Class
实例时注入的。我的Class
看起来像这样:
public class Class
{
private readonly Settings settings;
public Settings Settings { get { return settings; } }
public Class(Settings settings)
{
this.settings = settings;
}
}
我有一些看起来像这样的代码来获取Class
的实例。我知道这是service locator anti pattern,但由于其他限制,我们在这种情况下别无选择:
var settings = new Settings();
var instance = Ioc.Instance.Get<Class>("settings", settings);
我想重构它看起来像这样强大的类型,使用lambda来指定我提供的构造函数上的哪个参数:
var settings = new Settings();
var instance = Ioc.Instance.Get<Class>(x => x.settings, settings);
那么,这可能吗,代码会是什么样的?
答案 0 :(得分:2)
从概念上讲,缺乏工厂(工厂界面),因此应该引入它以避免直接使用容器。
Ninject Factory(工厂接口)扩展可用于创建实例,如下所示:
声明工厂界面:
public interface IFactory
{
Class Create(Settings settings);
}
向组合根添加绑定:
kernel.Bind<IFactory>().ToFactory();
使用工厂获取实例:
var settings = new Settings();
var factory = Ioc.Instance.Get<IFactory>();
var instance = factory.Create(settings);
请参阅ninject/ninject.extensions.factory了解替代方案。
答案 1 :(得分:2)
构造函数参数名称和表达式的问题是,当表达式覆盖构造函数的所有参数时,表达式才有效/完整。现在我想你想要注入一些参数(让ninject处理它们)和一个或两个特定参数你要传递一个值,让我们说它看起来像:
public interface IFoo { }
public class Foo : IFoo
{
public Foo(IServiceOne one, IServiceTwo two, string parameter) {...}
}
Ninject支持ctor表达式,但仅限于绑定,它们的工作原理如下:
IBindingRoot.Bind<IFoo>().ToConstructor(x =>
new Foo(x.Inject<IServiceOne>(), x.Inject<IServiceTwo>(), "staticArgument");
因此,您还必须指定IServiceOne
和IServiceTwo
,而不是仅指定您感兴趣的“staticArgument”。如果构造函数改变了怎么办?那么电话也需要调整!很多工作只是传递一个简单的参数。
现在,如果你仍然想要这样做,我建议您查看ToConstructor
代码,并为Get
调用创建一个类似的扩展程序,这将转换一些调用
IResolutionRoot.Get<IFoo>(x =>
new Foo(
x.Ignore<IServiceOne>(),
x.Ignore<IServiceTwo>(),
x.UseValue("mystring"));
到
IResolutionRoot.Get<IFoo>(new ConstructorArgument("parameter", "mystring"));
但是,我建议使用@Sergey Brunov的答案并使用Ninject.Extensions.Factory。现在我想你会说这不好,因为你仍然需要指定参数名称,...这不是重构安全和麻烦(没有代码完成......)。
但是,有一个问题的解决方案:您可以使用类型匹配参数,而不是使用“匹配”参数名称的构造函数参数。 好的,有一个问题。如果你有多个相同类型的参数,那么它就行不通了。但我认为这种情况很少,你仍然可以引入一个容器数据类来解决它:
public class FooArguments
{
string Argument1 { get; set; }
string Argument2 { get; set; }
}
现在你如何使用类型匹配? 有两种方式:
Func<string, IFoo>
工厂。只需将Func<string, IFoo>
注入您要创建的位置IFoo
。IInstanceProvider
(另请参阅http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/),以便您可以执行以下操作:public interface IFooFactory
{
IFoo Create([MatchByType]string someParam, string matchByName);
}
(==&gt;使用属性告诉工厂扩展如何将参数传递给Get<IFoo>
请求。)
答案 2 :(得分:0)
看下面的文章 - http://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
具体地
public static class Extensions
{
public static string GetPropertyName<T,TReturn>(this Expression<Func<T,TReturn>> expression)
{
MemberExpression body = (MemberExpression)expression.Body;
return body.Member.Name;
}
}
注意 - 此方法可能无法用于所有可能的方式,其中可以使用表达式来指示类的属性名称,因此可能需要根据您的需要进行增强(您需要它的通用性)。
但基本上,一旦你有了这个帮助方法,你的通话就会变成
var settings = new Settings();
Ioc.Instance.Get<Class>(GetPropertyName(x => x.settings), settings);