views:

111

answers:

1

Hi,

a possibly simple problem, but weird why I have no idea how to do it:

Unity (PRISM) and static methods. In this special case, an extension method. But in general, how do I access a "unity provided instance" in a static method. Think e.g. of a logging service I want to access to log some stuff I am doing inside a static method. Do I really have to pass the ref to the logging service when using it?

Example (close to actual problem)

public static void HookupPrismEvent(ref UIElement, ILogger log) {...}

Seems strange, I think I am missing somethings, like Container.Resolve (static resolve). Have not found anything, but container, unity or static are not the worlds best search terms. Perhaps I should just try it, but still, it feels kind of "strange"..

So any comments on HOW and IF to use DI in static methods?

Chris

EDIT - ok, current approach after answer: EDIT2, after thinking about it, removed container, providing "what needed"....

  public static void AttachPrismEvents(this UIElement element, IEventAggregator eA)
    {
        var ev = eA.GetEvent<KeyPressedEvent>();
        element.KeyDown += ((sender, e) => ev.Publish(e));
    }

or, with logging:

  public static void AttachPrismEvents(this UIElement element, ILogger log, IEventAggregator eA)
    {
        log.Debug("Doing stuff");
        var ev = eA.GetEvent<KeyPressedEvent>();
        element.KeyDown += ((sender, e) => ev.Publish(e));
    }
A: 

Static types and members are generally the enemy of all things DI.

Technically, you could have a static Resolve method, but that would not be DI, but rather a pattern known as Service Locator. However, many people (myself included) consider Service Locator an anti-pattern for several reasons:

  • It makes implicit assumptions about the usage requirements of an API (the caller is not made aware that a Service Locator must be correctly configured before the member can be invoked)
  • It becomes impossible to nest containers
  • It introduces a redundant dependency on the Service Locator itself

If you must have a static method, you should pass the dependency via Method Injection, but I think it is often more beneficial to reconsider the overall API design. Often, the desired functionality can be modeled as a member on one of the input parameters instead.

Mark Seemann
Ok, I think I will try to provide the container. I edit the post, perhaps take a look and tell me what you think. Thanks, Chris...
Christian
I liked your original proposition better (i.e. injecting the ILogger). That more clearly demarcates the responsibility of the method and prevents 'responsibility pollution', and it also keeps the method DI Container agnostic.
Mark Seemann
Yeah, but in this case I would have to inject the IEventAggregator and the ILogger. Maybe more if needed. Perhaps I provide my own facade for the container like IContainer or similar. Have to check, PRISM should support switching the container anyway, lets see how they do it...
Christian
It can be difficult to keep the number of dependencies down, but as a rule of thumb, I think it is a cause for consideration whenever a method needs too many dependencies. In that case, I start to consider whether the API violates the Single Responsibility Principle. Often, a redesign towards fewer dependencies will result in an overall enhancement of the object model.
Mark Seemann
You are probably right. I thought in the same direction when trying to imagine a case where my extension method needs more then 2, max 3 references, and it smells. Providing the container and doing a lot of fancy stuff really isn't such a good idea :-)
Christian