Developers exposed to ASP.NET are likely to be familiar with the so-called Provider pattern. You see it a lot in that part of the BCL: Role Provider, Membership Provider, Profile Provider, etc. Lots of text has already been written about Providers, but the reason I want to add yet another blog post on the topic is because once in a while I get the question on how it relates to Dependency Injection (DI).

Is Provider a proper way to do DI?

No, it has nothing to do with DI, but as it tries to mimic loose coupling I can understand the confusion.

First things first. Let's start with the name. Is it a pattern at all? Regular readers of this blog may get the impression that I'm fond of calling everything and the kitchen sink an anti-pattern. That's not true because I only make that claim when I'm certain I can hold that position, so I'm not going to denounce Provider as an anti-pattern. On the contrary I will make the claim that Provider is not a pattern at all.

A design pattern is not invented - it's discovered as a repeated solution to a commonly recurring problem. Providers, on the other hand, were invented by Microsoft, and I've rarely seen them used outside their original scope. Secondly I'd also dispute that they solve anything.

That aside, however, I want to explain why Provider is bad design:

  • It uses the Constrained Construction anti-pattern
  • It hides complexity
  • It prevents proper lifetime management
  • It's not testable

In the rest of this post I will explain each point in detail, but before I do that we need an example to look at. The old OrderProcessor example suffices, but instead of injecting IOrderValidator, IOrderCollector, and IOrderShipper this variation uses Providers to provide instances of the Services:

public SuccessResult Process(Order order)
{
    IOrderValidator validator = 
        ValidatorProvider.Validator;
    bool isValid = validator.Validate(order);
    if (isValid)
    {
        CollectorProvider.Collector.Collect(order);
        ShipperProvider.Shipper.Ship(order);
    }
 
    return this.CreateStatus(isValid);
}

The ValidatorProvider uses the configuration system to create and return an instance of IOrderValidator:

public static IOrderValidator Validator
{
    get 
    {
        var section = 
            OrderValidationConfigurationSection
                .GetSection();
        var typeName = section.ValidatorTypeName;
        var type = Type.GetType(typeName, true);
        var obj = Activator.CreateInstance(type);
        return (IOrderValidator)obj;
    }
}

There are lots of details I omitted here. I could have saved the reference for later use instead of creating a new instance each time the property is accessed. In that case I would also have had to make the code thread-safe, so I decided to skip that complexity. The code could also be more defensive, but I'm sure you get the picture.

The type name is defined in the app.config file like this:

<orderValidation 
  type="Ploeh.Samples.OrderModel.UnitTest.TrueOrderValidator,
        Ploeh.Samples.OrderModel.UnitTest" />

Obviously, CollectorProvider and ShipperProvider follow the sameā€¦ blueprint.

This should be well-known to most .NET developers, so what's wrong with this model?

Constrained Construction #

In my book's chapter on DI anti-patterns I describe the Constrained Construction anti-pattern. Basically it occurs every time there's an implicit constraint on the constructor of an implementer. In the case of Providers the constraint is that each implementer must have a default constructor. In the example the culprit is this line of code:

var obj = Activator.CreateInstance(type);

This constrains any implementation of IOrderValidator to have a default constructor, which obviously means that the most fundamental DI pattern Constructor Injection is out of the question.

Variations of the Provider idiom is to supply an Initialize method with a context, but this creates a temporal coupling while still not enabling us to inject arbitrary Services into our implementations. I'm not going to repeat six pages of detailed description of Constrained Construction here, but the bottom line is that you can't fix it - you have to refactor towards true DI - preferably Constructor Injection.

Hidden complexity #

Providers hide the complexity of their implementations. This is not the same as encapsulation. Rather it's a dishonest API and the problem is that it just postpones the moment when you discover how complex the implementation really is.

When you implement a client and use code like the following everything looks deceptively simple:

IOrderValidator validator = 
    ValidatorProvider.Validator;

However, if this is the only line of code you write it will fail, but you will not notice until run-time. Check back to the implementation of the Validator property if you need to refresh the implementation: there's a lot of things that can go wrong here:

  • The appropriate configuration section is not available in the app.config file.
  • The ValidatorTypeName is not provided, or is null, or is malformed.
  • The ValidatorTypeName is correctly formed, but the type in question cannot be located by Fusion.
  • The Type doesn't have a default constructor. This is one of the other problems of Constrained Construction: it can't be statically enforced because a constructor is not part of an abstraction's API.
  • The created type doesn't implement IOrderValidator.

I'm sure I even forgot a thing or two, but the above list is sufficient for me. None of these problems are caught by the compiler, so you don't discover these issues until you run an integration test. So much for rapid feedback.

I don't like APIs that lie about their complexity.

Hiding complexity does not make an API easier to use; it makes it harder.

An API that hides necessary complexity makes it impossible to discover problems at compile time. It simply creates more friction.

Lifetime management issues #

A Provider exerts too much control over the instances it creates. This is a variation of the Control Freak anti-pattern (also from my book). In the current implementation the Validator property totally violates the Principle of least surprise since it returns a new instance every time you invoke the getter. I did this to keep the implementation simple (this is, after all, example code), but a more normal implementation would reuse the same instance every time.

However, reusing the same instance every time may be problematic in a multi-threaded context (such as a web application) because you'll need to make sure that the implementation is thread-safe. Often, we'd much prefer to scope the lifetime of the Service to each HTTP request.

HTTP request scoping can be built into the Provider, but then it would only work in web applications. That's not very flexible.

What's even more problematic is that once we move away from the Singleton lifestyle (not to be confused with the Singleton design pattern) we may have a memory leak at hand, since the implementation may implement IDisposable. This can be solved by adding a Release method to each Provider, but now we are moving so far into DI Container territory that I find it far more reasonable to just use proper DI instead of trying to reinvent the wheel.

Furthermore, the fact that each Provider owns the lifetime of the Service it controls makes it impossible to share resources. What if the implementation we want to use implements several Role Interfaces each served up by a different Provider? We might want to use that common implementation to share or coordinate state across different Services, but that's not possible because we can't share an instance across multiple providers.

Even if we configure all Providers with the same concrete class, each will instantiate and serve its own separate instance.

Testability #

The Control Freak also impacts testability. Since a Provider creates instances of interfaces based on XML configuration and Activator.CreateInstance, there's no way to inject a dynamic mock.

It is possible to use hard-coded Test Doubles such as Stubs or Fakes because we can configure the XML with their type names, but even a Spy is problematic because we'll rarely have an object reference to the Test Double.

In short, the Provider idiom is not a good approach to loose coupling. Although Microsoft uses it in some of their products, it only leads to problems, so there's no reason to mimic it. Instead, use Constructor Injection to create loosely coupled components and wire them in the application's Composition Root using the Register Resolve Release pattern.


Comments

I have to disagree with your statement that the provider is not a pattern at all. The provider (in .NET) is a specific implementation of the bridge pattern as defined in "Design Patterns" by Gamma, Helm, Johnson, and Vlissides. I believe that this description has been lost over the past 10 years as people in the industry have resorted to calling it the "Provider Pattern."

When used correctly as a bridge pattern, the provider does actually solve reoccuring problems very eloquently. It decouples the interface from the implementation so that the implementation details are hidden from the client. You can extend the implementation by building on existing implementations to reduce the complexity. Complexity is only introduced as a by-product of bad design.

The notion that the provider interfers with testability is incorrect. Each individual implementation should be designed from the beginning to be testable. The bridge is not the entry point for testing the implementor. The bridge should only be responsible for forwarding client requests to its implementor. (Therefore the bridge should be testable as well.) I am not stating that there aren't providers out there that violate this priciple. I'm merely stating that providers which do this are poorly designed.
2011-06-27 16:43 UTC
I just reread the Bridge pattern and while I agree that Provider is a specialization of Bridge, I don't agree that this relationship goes the other way. If you read the description of Provider provided in the link above, you'll notice that it goes into very specific details on how a Provider should be implemented, including how it should be backed by the configuration system and that it must be created by clients by a static factory.

This goes way beyond what the Bridge pattern describes, so I hardly think you can equate the two.

Especially the part about being created by a static factory which can only read from the configuration system is testability poison.
2011-06-27 19:25 UTC
I disagree with your analysis. Provider model is a pattern.

I realize this is a year late, but I just found your article a few days ago via a reference on scott hanselmans blog (in comments). Here is my rebuttal.
http://candordeveloper.com/2012/06/26/provider-model-is-a-solid-pattern/
2012-06-29 11:31 UTC
Also going to throw in on the disagreement with your assertion:

"A design pattern is not invented - it's discovered as a repeated solution to a commonly recurring problem."

The design pattern concept does not provide an opinion on how they were arrived upon. I am always delighted when programming terminology gives me new insight into words. Kinda like "abstraction" -- I don't think I would even know the term if I were not a programmer, at least, not as well as I do.

However, programming has little to teach us about the words "design" and "pattern" that we do not already know. There is nothing mysterous or supernatural about them, even when combined. A pattern is anything that repeats in a predictable fashion.. and that's really all there is to it.

Whether or not the service provider is a good pattern, I am not sure, but I can be certain that it is, in fact, a pattern. Isn't the neuance you're describing subjective, anyway? Suppose that you invented it, and then I used it. Suppose also that a third developer "discovered" our usage. What then, does the universe unwind? My example is trivial, but scale it up and it makes a little more sense.

But, I do love your blog, thank you for the discussion.
2014-11-11 9:55 UTC

Luke, thank you for writing. Obviously, you are free to have your own opinions, and interpretation of various words. However, if we want to be able to discuss our trade in a concise manner, we need as precise a vocabulary as possible.

On this blog (and elsewhere), I strive to use a consistent and well-known jargon. Thus, when I use the term design pattern, I refer to it in a way that's compatible with the original description in Design Patterns. In the introduction (on page 2), Gamma et al. write:

"None of the design patterns in this book describes new or unproven designs. We have included only designs that have been applied more than once in different systems."
(Emphasis mine.) The point is that, using the established vocabulary, a design pattern most certainly implies that the pattern is a result of parallel evolution, and subsequently discovered and described as a common solution to a particular type of problem.

Although I grant that it's partially subjective, there's still an implicit value judgement in cataloguing a design pattern. While most patterns come with a set of known disadvantages, but those disadvantages tend to not outweigh the benefits. If they did, it wouldn't be a design pattern, but rather an anti-pattern. This, too, is a well-defined term:

"a commonly occurring solution to a problem that generates decidedly negative consequences."
This definition differs from a 'proper' design pattern because a design pattern has mostly beneficial consequences.

2014-11-14 20:32 UTC

I appreciate your effort to maintain consistent jargon and termonology. I have had dozens of heated arguments with my co-workers about individual words in our interfaces, all the while they're staring back at me with a look that says "omg dude, its just a word." I gather that you and I have that common.

Personally, I'm against the concept of jargon all together because it, by its nature, can only create confusion. By that I mean, if it is consistent with the English language, then its not jargon, its just English. Conversely, if it contradicts or conflicts with the English language, then its bad jargon anyway, and can only lead to confusion. I tend to err on the side of verbosity, and might have described the pattern as not being a "Beneficial Design Pattern".

In reading the paragraph you quoted from, I did not get the impression that the author gave an opinion on the origin of a pattern as a contributor to its validity. I think he is simply trying to disclaim the notion that the patterns described within the book are original ideas, and effort was made to only include patterns that have been implemented in the wild, and by more than one implementor. i.e. I did not write a book about my own patterns, or the clever things my co-workers have done.

"So although these designs are not new, we capture them in a new and accessible way: as a catalog of design patterns having a consistent format."

The service pattern certainly has been implemented by more than one implementor, sometimes identified by a different name, sometimes in such a way that provides more benefit than otherwise, and very likely before Microsoft introduced it to the world at-large.

My critique of your article was based solely on the language used and only because of provocativeness of your statement. It seemed intentionally unintuitive.

With all of that said, though, I concede my point. I do that mostly because you obviously have a superior understanding of design patterns than I do (by multitudes). Your blog is one of resources that first got me interested in the study of design patterns, and I've learned a great deal from it. I think it would be rather foolish for me to try to "take you on", as I'd certainly be embarrassed.

Besides that, I could not formulate an opinion on the supporting arguments you put forth as I reread the blog. I have not programmed in .NET in a very long time and I have trouble understanding if we're even referring to the same things. I fear that I am a bit out of my league.

My [perhaps misguided] interpretation of the "Service Pattern" comes from places wholy unrelated to .NET. I've seen the term used to describe a pattern akin to the "Adapter Pattern", where the term "Adapter" is replaced by "Service", and "Adaptee" is replaced by "Provider".

Implementor < Service < Provider (or ServiceProvider)

i.e.

Model < StorageService < MySQLProvider

Thank you for your thoughtful response.

2014-11-15 3:10 UTC

At the risk of coming across as a pedant, did you mean Provider when you wrote "Service Pattern"? At least, I'm not aware of any Service Pattern in the most commonly accepted pattern literature, but perhaps there's a book I should read.

The Provider design isn't the same as the Adapter pattern. The difference is, among others, that Providers rely on the .NET configuration system, as well as the Constrained Construction anti-pattern. Particularly its reliance on the .NET configuration system is what excludes it from being a proper pattern, because you couldn't possibly discover it on a different platform.

The Adapter pattern describes how to adapt one API to another API; while it's a useful pattern, it has a different concern than Provider. It also doesn't prescribe any particular method of initialization.

2014-11-15 20:53 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Wednesday, 27 April 2011 12:14:52 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Wednesday, 27 April 2011 12:14:52 UTC