2014-10-09 26 views
3

我不是太熟悉,實施lambda表達式和表情,但我已經習慣了這種語法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); 

所以,這是可能的,代碼是什麼樣子?

回答

2

從概念上講,缺少工廠(工廠界面),所以應該引入它來避免直接使用容器。

的Ninject工廠(工廠接口)擴展可用於創建實例,如下:

聲明一個工廠接口:

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的替代方案。

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); 
+0

這是否構造函數?在我的測試中,我只能看到課堂上的屬性? – 2014-10-10 09:36:42

2

與構造函數的參數名稱和表現的問題是,一個表達式是唯一有效/完全在它覆蓋的構造函數的所有參數。現在,我想你要注入一些參數(有ninject處理它們),以及您想傳遞一個值的一個或兩個特定的參數,比方說,它看起來像:

public interface IFoo { } 
public class Foo : IFoo 
{ 
    public Foo(IServiceOne one, IServiceTwo two, string parameter) {...} 
} 

Ninject支持構造函數表達式,但只有綁定和他們的工作是這樣的:

IBindingRoot.Bind<IFoo>().ToConstructor(x => 
    new Foo(x.Inject<IServiceOne>(), x.Inject<IServiceTwo>(), "staticArgument"); 

所以不是僅指定「staticArgument」你有興趣,你還必須指定IServiceOneIServiceTwo。如果構造函數改變呢?那麼通話也需要進行調整!很多工作只是傳遞一個簡單的參數。

現在,如果你仍然想這樣做,我建議有一個看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布魯諾夫的答案和使用Ninject.Extensions.Factory。現在我想你會說這是不好的,因爲你仍然必須指定參數名稱,..這不是重構安全和麻煩(沒有代碼完成...)。

但是,有問題的解決方案:不使用「匹配」參數名稱的構造函數參數,您可以使用類型匹配參數。好的,有一個問題。如果你有多個相同類型的參數,那麼它將無法工作。但我認爲這是罕的情況下,你仍然可以引入一個容器數據級來解決它:

public class FooArguments 
{ 
    string Argument1 { get; set; } 
    string Argument2 { get; set; } 
} 

現在你怎麼能使用類型匹配? 有兩種方法:

  1. 使用Func<string, IFoo>工廠。只需將Func<string, IFoo>注入到您想要創建的位置,然後IFoo即可。
  2. 延長工廠擴展。是的,你聽說過對;-)其實並不困難。你「只是」需要實現自定義IInstanceProvider(見http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/),這樣你就可以是這樣的:
public interface IFooFactory 
{ 
    IFoo Create([MatchByType]string someParam, string matchByName); 
} 

(==>使用屬性來告訴工廠擴展如何將參數傳遞到Get<IFoo>請求)。

+1

再次感謝您提供非常全面的答案。我使用Ninject工廠選項。儘管如果'setting' arg被重命名,它會中斷,至少魔術字符串已經消失。 – 2014-10-10 14:27:34

相關問題