代码之家  ›  专栏  ›  技术社区  ›  Jaguar

如何自动注入所有子对象

  •  2
  • Jaguar  · 技术社区  · 14 年前

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

    Injectable

    但是,我希望通过定义 对象,然后为每个对象和从中继承的每个类定义一个子对象 Foo

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

    谢谢你的建议

    编辑:

    目前我正在使用上面提到的代码,其中抽象类引用一个Id为“Injectable”的DI创建的对象。我想避免

    更新(带解决方案)

    考虑: 班级

    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();
       }
    }
    

    ...

    <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>
    

    BasePage 将拥有 FooProp 属性注入了我配置的内容。

    如果可以通过某种约定绑定来实现这一点就无关紧要了,但是我不想在代码中使用字符串和DI引用。

    多亏了 tobsen Erich Eichinger 首先,这在本机上不受支持,但解决方案不是很难看,也不是以一种不好的方式打破DI模式规范

    埃里克的解决办法是 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);
      }
    }
    

    在寻找功能(也许还有优雅)时,我发现 IHTTP模块 (我不想再添加一个类)您可以声明 基本页 像这样的

    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);
       }
    }
    

    它就像一个魅力,不需要任何额外的模块等。

    很高兴这也适用于UserControls(尽管生命周期中的事件不同,因为UserControls不存在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);
       }
    }
    

    5 回复  |  直到 7 年前
        1
  •  3
  •   Erich Eichinger    14 年前

    对不起,我只有有限的时间自动取款机,请平我,如果下面的描述应该是太短/抽象

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

    以下是解决方案的概要: 由于您的问题与ASP.NET WebForms相关,因此HttpModule是创建和配置.aspx页面的良好起点。

    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

    嗯,

        2
  •  2
  •   Community Egal    4 年前

    可以使用自动布线(请参见 5.3.6. Autowiring collaborators

    我想知道是谁负责创建那些从Foo派生的类的实例? 我认为应该让spring创建那些从Foo派生的Asp.Net页面类的对象。似乎Spring对Asp.net页面有广泛的支持(Spring.Web.support.PageHandlerFactory、Spring.Web.Services.WebServiceHandlerFactory等),我本来会发布一个适合您需要的配置示例,但我还没有使用Asp.net。所以这里有一些链接:

    ==开始编辑==

    为了回答这个问题,我需要知道子对象实例化的决策逻辑是什么。i、 e.具体的子对象何时/何地由谁请求?谁决定要创建的对象的具体类型? 如果您可以控制具体子类的实例化,则可以创建具体类型的实例,并告诉spring在之后设置属性:

    :将依赖项注入到提供的目标实例中。抽象对象定义的名称是目标实例的System.Type.FullName。此方法通常用于在开发人员控制之外实例化对象时,例如ASP.NET实例化web控件时以及WinForms应用程序创建UserControls时。

    void ConfigureObject(对象目标,字符串名称) :提供与前面列出的Configure方法相同的功能,但使用命名对象定义,而不是使用类型的全名。

    22.4.3. Injecting dependencies into HTTP handlers and handler factories . 但是,您必须告诉spring要实例化哪个objectdefinition,而且似乎有一种自动性,这在您的案例中可能很有用(取决于子类实例化的逻辑是什么)(也是1.3Spring文档的22.4.3):

    Spring的DefaultHandlerFactory使用 .NET类System.Web.UI.SimpleHandlerFactory 创建处理程序实例和 通过使用名称与请求URL的文件名匹配的对象定义来配置每个实例 . DemoHandler.ashx的抽象对象定义就是这种方法的一个例子。您还可以配置实现IHttpHandler接口的标准类,如上面MyCustomHttpHandler类的示例所示。

    ==结束编辑==

    ==开始编辑==

    Erich Eichinger(spring.net开发人员之一)似乎 once had exactly the same problem as you do . 看来最后他不得不跟你一样跟集装箱说话。正如Erich在论坛帖子中写道的,他不太乐意直接依赖IoC容器。也许他提出的解决方案已经整合到Spring中了。我会尽力弄清楚的。

    ==End2ndInit==

        3
  •  0
  •   decyclone    14 年前

    我认为您建议的是一个类,它从配置文件中获取属性列表。如果这是真的,你应该调查 Code Generators . 您可以创建一个简单的代码生成器来读取配置文件并在给定的子类中创建属性。 Code Smith 应该是个好的开始。

        4
  •  0
  •   Community Egal    4 年前

    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框架配置为生成Baz并填充其Bar属性。

    我不知道Spring的具体实现,但这是我要研究的主要方向。

    当我谈到“约定”约束时,我指的是自动布线。托布森的回答对春季汽车布线有很好的借鉴作用。它似乎比我一直使用的Ninject更难。但是,通过查看这些文档,您似乎可以告诉框架自动将值注入到具有指定名称或类型的任何公共属性中。在我上面的示例中,您可以告诉它应该注入任何名为“Bar”的属性。因为Foo的所有子类都包含一个名为Bar的属性,所以它们都会被注入这个值。这需要对配置文件进行一次性更改,之后的一切都应该“正常工作”

    然而,我得到的印象是,问题的真正根源是您没有以正确的方式使用依赖注入。托布森也知道了。如果你说 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 . 学习识别依赖注入的模式和反模式需要一点时间和实践,但是一旦你识别了,这是非常有益的。

        5
  •  -1
  •   Jeppe Stig Nielsen    14 年前

    声明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"/>