开发者

possible GetObjectsOfType replacement

开发者 https://www.devze.com 2023-02-11 04:45 出处:网络
I have this small piece of code var idObjects = Spring.Context.Support.ContextRegistry.GetContext() .GetObjectsOfType(typeof (ICustomInterfaceThatDoesSomething));

I have this small piece of code

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

Is there a way i can avoid this by having spring.net automatically inject the singletons to a property i declare, like an array of ICustomInterfaceThatDoesSomething?

The only reason i want something like this is because i want to kill the .dll dependency on the project and this is the single point 开发者_如何学编程of usage.


You could also use method injection:

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

In the web app add a class that implements 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();
    }
}

And configure method injection in you web app's object definitions:

  <objects>

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

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

  </objects>

I do feel that it would be better to use the CommonServiceLocator (since service location is what you're doing), but using method injection this way, you don't need to introduce an additional reference to SharedLib.


The interesting part of your question to me is: how to use service locator functions (such as spring.net's IApplicationContext.GetObjectsOfType(...)) from within your library, without introducing a dependency on a specific IoC container.

As stated in the question, this is useful, because we want to build libraries that do not force the consumer to use a specific IoC container. However, we still want to use an IoC container, because it eases the development of our library. This dilemma is nicely described by Jeremy Miller in the post "It’s time for IoC Container Detente".

His blog post has led to a small project on codeplex called CommonServiceLocator. This project specifies an IServiceLocator interface for service location, that is implemented by many popular IoC containers, including Spring.NET.

The IServiceLocator defines a IEnumerable<TService> GetAllInstances<TService>(); method, which basically is what is asked for in your question.

When your library needs service locator functionality, you can take a dependency on the CommonServiceLocator library and you and other consumers can wire it up using your IoC container of choice.

And guess what: Spring.NET adapter implements IEnumerable<TService> GetAllInstances<TService>(); using GetObjectsOfType(serviceType);.


I'll leave this answer here for future reference, but I like my other answer better.

The original answer is rather lenghty and very specific to the example in the question.

I don't think there is a configuration equivalent to GetObjectsOfType(...).

However, isn't it very easy to get rid of the Spring.net dependency?

Let me see if I understand correctly:

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

We want to get rid of the last dependency, because we want to be able to use sharedLib in combination with another DI container. In sharedLib we have a class that needs to signal all ICustomInterfaceThatDoesSomething implementations to do something. For this purpose I'd create:

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 used to contain the Spring dependency, but now it's Spring Free. Now I have two options when wiring up sharedLib with regard to MySomethingManager:

  1. using property injection on MySomethingManager.SomethingDoers with a List<ICustomInterfaceThatDoesSomething>
  2. using constructor injection with an IMySomethingDoerProvider implementation

Both can be done using Spring and many other DI containers. You can use the first approach if you don't mind listing all ICustomInterfaceThatDoesSomething in the configuration.

If you want magical GetObjectsOfType(...) code, you can use the features of your DI container to create a IMySomethingDoerProvider.

When using Spring, the second approach would require to create:

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

Which you can place in a project that depends on sharedLib. Since your webApp already depends on Spring.Core, you could place MyProvider there to get you started quickly.

Notes

If DoSomething is called once per instance, you might consider specifying an initialization method instead.


thanx to Marijin's insight this has been nailed!

Firstly consider this all-purpose utility class

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

declare it with this configuration sample

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

and finally inject to the required target

What is trully great with this methodology is that the underlying GetObjectsOfType is not going to be called until you actually execute GetAllICheImplentations() (unless you try to execute it during spring init which is not going to bode well)

0

精彩评论

暂无评论...
验证码 换一张
取 消