2010-06-30 13 views
2

考慮這個抽象類如何注入到所有子對象自動

public abstract class Foo 
{ 
    public Injectable Prop {get;set;} 
} 

我有我想提高,同時重構爲了簡單的應用程序。 我有超過100個類調用一些相同的東西(例如類Injectable),我認爲這種行爲可以抽象並設置爲一個基類,每個人都會從這個基類繼承,以刪除複製/粘貼代碼。

但是,我想避免在彈簧配置文件中複製/粘貼代碼,方法是定義一個Injectable對象,然後定義從Foo繼承的子對象foreach和每個類。

我正在尋找一種方法來設置抽象類的屬性,然後通過配置自動獲取它們的所有子元素。我想避免讓抽象類是這樣的:

public abstract class Foo 
{ 
    public Injectable Prop 
    { 
     get { return (Injectable)ContextRegistry.GetContext()["Injectable"]; } 
    } 
} 

感謝您的任何建議

編輯: 以使事情更復雜的子類是ASP.NET頁面,所以我有限控制它們是如何生成的。

當前我正在使用上面提到的代碼,其中抽象類使用Id「Injectable」來引用DI創建的對象。我想避免

UPDATE(帶解決方案)

考慮: 類

public abstract class BasePage : System.Web.UI.Page 
{ 
    public IInjectable FooProp {get;set;} 
} 

public abstract class BaseControl : System.Web.UI.UserControl 
{ 
    public IInjectable FooProp {get;set;} 
} 

public partial class ChildPage : BasePage 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     FooProp.DoSomeThing(); 
    } 
} 

public partial class ChildControl : BaseControl 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     FooProp.DoSomeThing(); 
    } 
} 

春天CFG ...

<object id="Injectable" type="ConcreteInjectable"> 
    <property name="SomeProp" value="Injected!!" /> 
</object> 
<!--The requirement is to declare something like:--> 
<object type="BasePage" abstract="true"> 
    <property name="FooProp" ref="Injectable /> 
</object> 
<!--it works for usercontrols too--> 
<object type="BaseControl" abstract="true"> 
    <property name="FooProp" ref="Injectable /> 
</object> 

和效果將爲每個的繼承者將有FooProp屬性注入我配置的內容。

它無關緊要,如果這可以通過某種約定結合來實現,但我不希望從我的代碼中使用的字符串和使用DI引用。

月2日更新和解決方案 感謝tobsenErich Eichinger發現該溶液: 首先,這不是原生支持,但解決的辦法也不是很醜陋的,也沒有一個壞的方式打破DI模式規範

現在,以上所有(在更新) 埃裏希的解決方案,這是給需要Spring的配置,使一個IHttpModule像這樣:

public class PageModuleInjecter : IHttpModule 
{ 
    public void Dispose() {} 

    public void Init(HttpApplication context) { 
    context.PreRequestHandlerExecute += context_PreRequestHandlerExecute; 
    } 

    void context_PreRequestHandlerExecute(object sender, EventArgs e) { 
    IHttpHandler handler = ((HttpApplication)sender).Context.Handler; 
    if (handler is BasePage) 
     Spring.Context.Support.WebApplicationContext.Current 
      .ConfigureObject(handler, typeof(BasePage).FullName); 
    } 
} 

就是這樣! (不要忘記在網上註冊模塊。配置總是)

在搜索的功能(也許優雅),我發現,而是採用了IHttpModule(餘did't要添加另一個類),你可以聲明BasePage這樣

public abstract class BasePage : System.Web.UI.Page 
{ 
    public IInjectable FooProp {get;set;} 

    protected override OnPreInit(EventArgs e) 
    { 
     Spring.Context.Support.WebApplicationContext.Current 
      .ConfigureObject(this, typeof(BasePage).FullName); 
     base.OnPreInit(e); 
    } 
} 

和它就像一個魅力,而不需要任何附加的模塊等

欣然這適用於用戶控件以及(儘管在生命週期中的不同的事件,因爲OnPreInit不爲用戶控件存在):

public abstract class BaseControl : System.Web.UI.UserControl 
{ 
    public IInjectable FooProp {get;set;} 

    protected override OnInit(EventArgs e) 
    { 
     Spring.Context.Support.WebApplicationContext.Current 
      .ConfigureObject(this, typeof(BaseControl).FullName); 
     base.OnInit(e); 
    } 
} 

感謝收看!

回答

3

抱歉,我只有有限的時間內ATM,PLZ ping通我,如果下面的描述應該是太短/抽象

首先,目前沒有可用沒有這樣的功能。但通過幾行代碼,您可以自行完成。

一般來說,下面我將追求將特定類映射到用於配置實例的對象定義的想法。沿

if (object is MyBaseClass) 
    applicationContext.Configure(object, "myObjectDefinitionName"); 

東西線這裏有一個解決方案的概要: 由於您的問題是,ASP.NET Web窗體相關的,一個HttpModule是一個很好的起點掛接到創建和配置的.aspx頁面。 這個想法是編寫自己的IHttpModule,它在執行之前執行頁面配置。基本上所有你需要的是

public class MyBaseClassConfigurationModule : IHttpModule { 

    private System.Collections.Generic.Dictionary<Type, String> typeObjectDefinitionMap; 

    public void Dispose() {} 

    public void Init(HttpApplication context) { 
    context.PreRequestHandlerExecute += context_PreRequestHandlerExecute; 
    } 

    void context_PreRequestHandlerExecute(object sender, EventArgs e) { 
    IHttpHandler handler = ((HttpApplication)sender).Context.Handler; 
    foreach(Type t in typeObjectDefinitionMap.Keys) { 
     if (t.IsAssignableFrom(app.Context.Handler.GetType)) { 
      Spring.Context.Support.WebApplicationContext.Current 
       .ConfigureObject(handler, typeObjectDefinitionMap[t]); 
     } 
    } 
    } 
} 

並配置你的模塊根據22.4.2. Injecting dependencies into custom HTTP modules

心連心, 埃裏希

0

我知道你在提議是一個類,從配置文件獲取的屬性列表。如果這是真的,你應該看看Code Generators。您可以創建一個簡單的代碼生成器來讀取配置文件並在給定的子類中創建屬性。 Code Smith應該是一個好的開始。

+0

我建議的是一些Spring.NET功能(如果存在的話),配置「知道」只有抽象類,並從那裏確認孩子的存在並注入到每個孩子配置的屬性 – Jaguar 2010-07-04 15:14:20

0

大多數依賴注入框架允許通過「約定」進行注入綁定。例如,你可以在屬性上放置一個屬性來標記它以便注入,當DI框架構造一個類時,它會知道向該屬性注入一個值。

public abstract class Foo 
{ 
    [Inject] 
    public Bar Bar {get; set;} 
} 

public class Baz : Foo 
{ 
    ... 
} 

public class SomeUtil 
{ 
    Baz _baz; 
    public SomeUtil(Baz baz) 
    { 
     _baz = baz; 
    } 
} 

在上面的例子中,如果我們可以假定SomeUtil類是由依賴注入產生的DI框架可被配置成生成和巴茲填充其酒吧屬性。

我不知道春天的具體實施,但是這是我想看看在大方向。

更新

當我談到通過結合「公約」,我是指自動佈線。 Tobsen的答案在Spring中對自動佈線有一些很好的參考。這似乎比Ninject更困難,這正是我一直在使用的。但是,通過瀏覽這些文檔,您可以告訴框架自動將值注入具有指定名稱或類型的任何公共屬性。在我上面的例子中,你可以告訴它,應該注入任何名爲「Bar」的屬性。由於Foo的所有子類都包含一個名爲Bar的屬性,因此它們都將注入此值。這需要對配置文件進行一次性更改,並且之後的所有內容都應該「正常工作」。

我得到的印象,但是,你的問題的真正根源在於不使用依賴注入相當的正確途徑。 Tobsen也接受了這一點。如果你曾經說過new Baz(),這是依賴注入的反模式。相反,你的依賴應該涓流成代碼中可能的最高點(什麼DI書所說的「上下文根」,在那裏你會實際上有代碼要求您需要依賴事情是這樣的:

public static void Main() 
{ 
    var someUtil = (SomeUtil)ContextRegistry.GetContext()["SomeUtil"]; 
    someUtil.DoSomething(); 
} 

在研究如何構建SomeUtil的過程中,Spring會發現它首先需要一個Baz,它會注意到Baz有一個Bar屬性,所以它會創建一個Bar並將其注入到該屬性中,然後將Baz傳遞給SomeUtil的構造函數,然後返回它剛剛創建的SomeUtil。

如果您對此不完全清楚,我強烈建議您閱讀a good book about Dependency Injection。學習識別依賴注入的模式和反模式需要一點時間和練習,但是一旦你做了,它就會很有收穫。

+0

這需要我配置該抽象類的每個孩子。我們正在研究100 ++類,最終結果將是配置文件中的複製/粘貼代碼,而不是代碼中的代碼。 – Jaguar 2010-07-04 15:17:28

+0

它確實不應該要求每個子類的顯式配置。看我的更新。Tobsen走在正確的軌道上,比我更瞭解Spring,所以一定要回答他的問題。 – StriplingWarrior 2010-07-06 17:23:34

2

您可以使用自動裝配(參見spring.net文檔中的5.3.6. Autowiring collaborators),通過彈簧自動設置Injectable-Property,而無需班級知道有關彈簧的任何信息。

你寫了「我想避免讓這樣的抽象類」,這是一個明智的選擇,因爲你不應該談論你的控制容器的反轉。

我想知道是誰來負責創建Foo派生類的實例? 我想你應該讓spring創建這些從Foo派生的Asp.Net頁面類的對象。看來Spring對Asp.net頁面(Spring.Web.Support.PageHandlerFactory,Spring.Web中的Spring.Web.Services.WebServiceHandlerFactory等)有廣泛的支持 - 我會發佈一個適合您需求的示例配置,但我沒有尚未使用ASP.Net。因此,不是這裏有一些鏈接:

你也應該看看春天的網絡實例是源代碼分發的一部分。

== == BeginEdit

爲了回答這個問題,我需要知道的決策邏輯是什麼,孩子們的實例。即什麼時候/在哪裏是由誰請求的具體子對象?誰決定要創建的對象的具體類型? 如果你有控制權的具體子類的實例,你可以創建具體類型的實例,並告訴春事後設置屬性:

無效ConfigureObject(對象目標):注入一個依賴進提供的目標實例。抽象對象定義的名稱是目標實例的System.Type.FullName。當對象在開發人員的控制之外實例化時,通常使用此方法,例如,當ASP.NET實例化Web控件時,以及WinForms應用程序創建UserControl時。

void ConfigureObject(object target,string name):提供與先前列出的Configure方法相同的功能,但使用命名對象定義而不是使用該類型的全名。

這將保留您的對象POCO,並且您只有一個對象創建點,並且具有對Spring的依賴關係。

如果邏輯實例化的類型可以通過頁面請求的正則表達式解決,Spring也有這樣的解決方案:22.4.3. Injecting dependencies into HTTP handlers and handler factories。然而,你必須告訴spring哪個對象定義要實例化,並且似乎有一個可能對你的案例有用的自動操作(取決於你的子類的實例化邏輯是什麼)(也是1.3 spring文檔的22.4.3):

Spring的DefaultHandlerFactory使用了.NET類System.Web.UI的。SimpleHandlerFactory來創建處理程序實例,並且通過使用名稱與請求URL的文件名相匹配的對象定義來配置每個實例。 DemoHandler.ashx的抽象對象定義就是這種方法的一個例子。您還可以配置實現IHttpHandler接口的標準類,如以上示例中MyCustomHttpHandler類所示。

== == EndEdit中

== == Begin2ndEdit

看來,埃裏希Eichinger(的spring.net開發商之一)once had exactly the same problem as you do。看起來他最終不得不像你一樣跟容器說話。正如Erich在論壇主題中所寫的,他不太樂意直接依賴IoC容器。也許他提出的解決方案已經集成到Spring中。我會盡力找出答案。

== == End2ndInit

+0

thx但我很多次閱讀參考文檔,我仍然無法弄清楚如何不在配置中聲明孩子,而是隻聲明抽象類 – Jaguar 2010-07-06 11:38:44

+1

我編輯了答案並提出了幾個問題,會到達那裏... – tobsen 2010-07-06 17:19:28

+0

檢查我的更新,孩子們是ASP.NET頁面,所以我沒有控制他們的instatiation – Jaguar 2010-07-07 14:50:28

-1

如果我理解正確的問題,解決的方法很簡單。

聲明正是像你這樣的BasePage的對象,並添加到它abstract="true"屬性:

<object id="BasePage" type="BasePage" abstract="true"> 
    <property name="FooProp"> 
    <object type="ConcreteInjectable"><property.... /></object> 
    </property> 
</object> 

所有來源的情況下,應配置如下所示:

<object type="somePage" parent="BasePage"/> 

如果你不想要分別配置每個實例,您應該考慮使用MEF,這使得此任務更容易預製。

相關問題