可能的GetObjectsOfType替换

时间:2011-02-17 08:45:15

标签: c# asp.net dependency-injection ioc-container spring.net

我有一小段代码

var idObjects = Spring.Context.Support.ContextRegistry.GetContext()
                      .GetObjectsOfType(typeof (ICustomInterfaceThatDoesSomething));
foreach (ICustomInterfaceThatDoesSomething icitds in idObjects.Values)
      icitds.DoSomething();

有没有办法可以避免这种情况,让spring.net自动将单例注入我声明的属性,就像ICustomInterfaceThatDoesSomething的数组一样?

我想要这样的事情的唯一原因是因为我想杀死项目的.dll依赖项,这是单点使用。

4 个答案:

答案 0 :(得分:2)

您也可以使用method injection

在sharedLib中:

public class MyService
{
    public void ProcessAll()
    {
      foreach (ICustomInterfaceThatDoesSomething icitds in GetAllImplementers())
        icitds.DoSomething();
    }

    protected virtual IEnumerable<ICustomInterfaceThatDoesSomething> GetAllImplementers()
    {
      // note that the Spring dependency is gone
      // you can also make this method abstract, 
      // or create a more useful default implementation
      return new List<ICustomInterfaceThatDoesSomething>(); 
    }
}

在网络应用中添加一个实现GetAllImplementers()的类:

public class ServiceLocatorImplementer : IMethodReplacer
{
    protected IEnumerable<ICustomInterfaceThatDoesSomething> GetAllImplementers()
    {
        var idObjects = Spring.Context.Support.ContextRegistry.GetContext()
            .GetObjectsOfType(typeof(ICustomInterfaceThatDoesSomething));

        return idObjects.Values.Cast<ICustomInterfaceThatDoesSomething>();
    }

    public object Implement(object target, MethodInfo method, object[] arguments)
    {
        return GetAllImplementers();
    }
}

在Web应用程序的对象定义中配置方法注入:

  <objects>

    <object name="serviceLocator" 
            type="WebApp.ServiceLocatorImplementer, WebApp" />

    <object name="service" type="SharedLib.MyService, SharedLib">
      <replaced-method name="GetAllImplementers" replacer="serviceLocator" />
    </object>

  </objects>

我觉得使用CommonServiceLocator会更好(因为服务位置就是你正在做的事情),但是这样使用方法注入,你不需要引入{{ {1}}。

答案 1 :(得分:1)

我会在这里留下这个答案以供将来参考,但我更喜欢我的other answer

原始答案相当长,而且对问题中的例子非常具体。

我认为没有相当于GetObjectsOfType(...)的配置。

然而,摆脱Spring.net依赖是不是很容易?

让我看看我是否理解正确:

// sharedLib contains ICustomInterfaceThatDoesSomething
// by "-->" I mean "depends on"

webApp --> Spring.Core, Spring.Web
webApp --> sharedLib  

sharedLib --> Spring.Core  // only to call GetObjectsOfType(...) on Spring container

我们希望摆脱最后的依赖关系,因为我们希望能够将sharedLib与另一个DI容器结合使用。在sharedLib中,我们有一个类需要发出所有ICustomInterfaceThatDoesSomething实现的信号来执行某些操作。 为此我要创建:

MySomethingManager
{
  public MySomethingManager() {}

  public MySomethingManager(IMySomethingDoerProvider prov) 
  { // init SomethingDoers }

  IList<ICustomInterfaceThatDoesSomething> SomethingDoers { get; set; }

  void SignalAllToDoSomething()
  {
    foreach (var doer in Provider.SomethingDoers )
      doer.DoSomething();
  }
}

IMySomethingDoerProvider
{
  IList<ICustomInterfaceThatDoesSomething> GetAll();
}

MySomethingManager曾用于包含Spring依赖项,但现在它是Spring Free。 现在,在关于sharedLib

MySomethingManager接线时,我有两个选项
  1. MySomethingManager.SomethingDoers上使用List<ICustomInterfaceThatDoesSomething>
  2. 使用属性注入
  3. 使用带有IMySomethingDoerProvider实现的构造函数注入
  4. 两者都可以使用Spring和许多其他DI容器完成。您可以使用第一种方法 如果您不介意在配置中列出所有ICustomInterfaceThatDoesSomething

    如果您需要神奇的GetObjectsOfType(...)代码,可以使用DI容器的功能来创建IMySomethingDoerProvider

    使用Spring时,第二种方法需要创建:

    MySomethingDoerSpringProvider: IMySomethingDoerProvider
    {
      IList<ICustomInterfaceThatDoesSomething> GetAll() 
      {
        // use Spring.Context.Support.ContextRegistry.GetContext()
        //                  .GetObjectsOfType(typeof (ICustomInterfaceThatDoesSomething));
      }
    }
    

    您可以将其置于依赖sharedLib的项目中。由于您的webApp已经依赖于Spring.Core,因此您可以将MyProvider放在那里以便快速入门。

    备注

    如果每个实例调用一次DoSomething,您可以考虑指定一个初始化方法。

答案 2 :(得分:1)

您问题的有趣部分是:如何在库中使用服务定位器函数(例如spring.net的IApplicationContext.GetObjectsOfType(...)),而不会引入对特定IoC容器的依赖。

正如问题中所述,这很有用,因为我们希望构建不强制使用者使用特定IoC容器的库。但是,我们仍然希望使用 IoC容器,因为它简化了我们库的开发。杰里米·米勒在帖子"It’s time for IoC Container Detente"中很好地描述了这种困境。

他的博客帖子导致small project on codeplex called CommonServiceLocator。该项目为服务定位指定IServiceLocator interface,由许多流行的IoC容器实现,包括Spring.NET。

IServiceLocator定义了IEnumerable<TService> GetAllInstances<TService>();方法,基本上是您问题中要求的方法。

当您的库需要服务定位器功能时,您可以依赖CommonServiceLocator库,您和其他消费者可以使用您选择的IoC容器连接它。

猜猜:Spring.NET适配器使用IEnumerable<TService> GetAllInstances<TService>();实现GetObjectsOfType(serviceType);

答案 3 :(得分:0)

感谢Marijin的洞察力已经被钉死了!

首先考虑这个通用实用程序类

public class ServiceLocatorImplementer : IMethodReplacer
{
    private readonly Type _forType;
    public ServiceLocatorImplementer(Type forType)
    {
        this._forType = forType;
    }

    protected IEnumerable GetAllImplementers()
    {
        var idObjects = Spring.Context.Support.ContextRegistry.GetContext()
            .GetObjectsOfType(_forType);

        return idObjects.Values;
    }

    public object Implement(object target, MethodInfo method, object[] arguments)
    {
        return GetAllImplementers();
    }
}

使用此配置示例

声明它
<object id="FindInterfaceUsages" type="ServiceLocatorImplementer, SpringDependendAssembly">
    <constructor-arg name="forType">
        <object type="System.Type" factory-method="GetType">
            <constructor-arg type="string" name="typeName" value="Foo.Bar.IChe, NONSpringDependendAssembly" />
            <!-- i use the strict overload -->
            <constructor-arg type="bool" name="throwOnError" value="true" />
            <constructor-arg type="bool" name="ignoreCase" value="false" />
        </object>
    </constructor-arg>
</object>

<object id="MightyPirate" type="Foo.Pirates, NONSpringDependendAssembly">
    <replaced-method name="GetAllICheImplentations" replacer="FindInterfaceUsages" />
</object>

最后注入所需的目标

这种方法的优点在于,在您实际执行GetAllICheImplentations()之前,基础GetObjectsOfType 将被调用(除非您尝试在spring init期间执行它而不是好好预示)