posts - 221,  comments - 12451,  trackbacks - 332

During the following post I'm going to explain a design pattern widely used during software development processes and how it fits into the .NET ecosystem. This pattern would probably fits into the structural patterns group and it's mainly used to decouple components, so abstractions and concrete implementations can vary independently.

Using the provider pattern in .NET (and most object oriented languages) is pretty easy. We need the following artifacts:

  • A provider definition, used as a contract that specifies what it should do
  • One or multiple provider implementations of the declared contract
  • A consumer willing to make use of the provider and it's implementations

Imaging the following real life scenario: There is a basic definition of what a car should do (provider definition) and what is considered a car: it must have 4 wheels, an engine, steering wheel, etc.. in the market you are able to find lot of implementations following the actual definition of a car (implementations of the provider), and we, as consumers, can make use of the knowledge and methods defined in the provider contract to change the implementation we use without affecting our behavior (we are able to switch cars and use the common definition of it, as a car, to drive any of the implementations).

So know, imagine it in the context of software development. We have an interface that defines a set of properties and methods that our provider must implement; we then have one or multiple implementations of that interface available; and of course, any consuming application can use the interface to switch between implementations without being affected. Have a look to the following diagram:

This is pretty awesome and practical when we are willing to create componentized and decoupled applications. We just use interfaces. This pattern has been widely used in the new ASP.NET 2.0 to create the application services provider infrastructure. For example, the MembershipProvider can be defined either as SqlMembershipProvider or as a custom OracleMembershipProvider; they both provide the same functionality but with different behavior (one uses SQL Server as repository while the other is using Oracle databases)

This pattern itself, IMHO, can dramatically increase the quality of your architecture because of the modularity and simplicity it provides.

One of the hidden treasures of .NET is the existence of a Provider Model Framework that allows you to create custom Providers for anything (not only extensions of ASP.NET Providers, but any kind of .NET application or service)

 

Using .NET Framework 2.0 Provider Model

So let's start writting some code. To create a provider we first have to create a public abstract class that defines our contract. This class must implement the System.Configuration.ProviderBase abstract class and it should contain the definition of methods and properties used as the "contract" of our provider.

1 public abstract class ImageProvider : ProviderBase 2 { 3 public abstract bool CanSaveImages { get; } 4 public abstract Image GetImage(int id); 5 public abstract void SaveImage(Image image); 6 }

We then need to add the actual implementations of the provider. In this case, we are going to add an image provider for the file system and an extra one for SQL Server.

1 public class FileSystemImageProvider : ImageProvider 2 { 3 public override bool CanSaveImages 4 { 5 get 6 { 7 return true; // As we allow to save images 8 } 9 } 10 11 public override Image GetImage(int id) 12 { 13 // Some witty code to get the image from a folder in the FileSystem 14 return null; 15 } 16 17 public override void SaveImage(System.Drawing.Image image) 18 { 19 // Some witty code to save the image to a folder in the FileSystem 20 } 21 } 22 23 24 public class SqlImageProvider : ImageProvider 25 { 26 public override bool CanSaveImages 27 { 28 get 29 { 30 return true; // As we allow to save images 31 } 32 } 33 34 public override Image GetImage(int id) 35 { 36 // Some witty code to get the image from a folder in the FileSystem 37 return null; ; 38 } 39 40 public override void SaveImage(System.Drawing.Image image) 41 { 42 // Some witty code to save the image to a folder in the FileSystem 43 } 44 }

And finally, we need to create the service that will use the ImageProvider. It's is coupled to the contract definition of our provider and not to it's implementations, so it knows what to ask for but no actually how or where will it be done. The service just initializes the provider using the LoadProvider() method (it should read the provider from the config file; have in mind that here it's hardcoded for instructional purposes and you only need to change the LoadProvider method to achieve that goal)

1 public class ImageService 2 { 3 private ImageProvider _provider; 4 5 public ImageProvider Provider 6 { 7 get 8 { 9 return _provider; 10 } 11 } 12 13 public ImageService() 14 { 15 LoadProvider(); 16 } 17 18 public void LoadProvider() 19 { 20 ProviderSettings ps = new ProviderSettings("FileSystemImageProvider", "FileSystemImageProvider_Type_Assembly"); 21 _provider = ProvidersHelper.InstantiateProvider(ps, typeof(FileSystemImageProvider)) as ImageProvider; 22 } 23 24 public Image GetImage(int id) 25 { 26 return _provider.GetImage(id); 27 } 28 29 public void SaveImage(Image image) 30 { 31 _provider.SaveImage(image); 32 } 33 }

The main caveat of using the .NET Provider Model is that you must reference the System.Web assembly in, probably, non-web projects. This is needed in order to access the ProvidersHelper class used to instantiate the provider collection. And it also forces the application to use the System.Configuration namespace and classes to read the provider configuration located in the web.config or app.config files.

You'll have to deal with the types and assembly names where those types are defined to create the ProviderSettings instance needed by the ProvidersHelper.InstantiateProvider method. It's maybe not recommended to use all this "web" references in a desktop or service context; so there's a clean and elegant alternative for the OO purists :)

 

The pure OO implementation

So, we need to get rid of the System.Web reference and make the provider pattern work without using the .NET Framework 2.0 model. The first thing we need to do is to refactor provider's abstract class to an interface (or leave it as a class, it's up to you, but remove the ProviderBase inheritance and any reference to the System.Configuration namespace) and change the concrete providers implementations to make them use the newly created interface. The class diagram should look similar to:

First, we should create the following code for the provider contract:

1 public interface IImageProvider 2 { 3 bool CanSaveImages { get; } 4 Image GetImage(int id); 5 void SaveImage(Image image); 6 }

And of course change the signature of the concrete provider implementations for FileSystemImageProvider and SqlImageProvider so they use the new contract:

1 public class FileSystemImageProvider : IImageProvider 2 { 3 // Implementation of the provider code, only signature of the class changed 4 } 5 6 7 public class SqlSystemImageProvider : IImageProvider 8 { 9 // Implementation of the provider code, only signature of the class changed 10 }

The ImageService still almost the same, we only need to change LoadProvider's code so it creates an instance of the desired concrete implementation without using the ProvidersHelper class. Something like this may work perfectly:

1 public void LoadProvider() 2 { 3 _provider = Activator.CreateInstance(typeof(FileSystemImageProvider)) as IImageProvider; 4 }

Any conclusion?

As you can see, the approach it's quite similar for both methods and it's really simple to introduce it in most of actual software projects and architectures. It provides ease of installation, highly decoupled systems and modularity both in development and deployment.

There's no excuse to avoid this pattern, as far as it's possible and pragmatic to use it in the context of your architecture :)

Hope this helps anyone.

posted on Friday, December 01, 2006 7:19 AM

Post a new comment about this topic
Title  
Name  
Url

Comments   
Protected by Clearscreen.SharpHIPEnter the code you see: