RSS 2.0
# Saturday, February 03, 2007

Since we always see the “Provider Model” used in the context of Asp.Net applications (Membership Provider, Roles Provider, Etc.) we often think of this as something that is available only in the Asp.Net world.  It was recently mentioned to me that there are no good source code examples for custom providers in the Winforms space and that the Asp.Net examples utilize the System.Web.Configuration.ProvidersHelper class to instantiate the provider. 

 

While there are several other blogs that talk about bits and pieces of this subject, I thought I might create a start-to-finish Windows Forms Application that illustrates how one can effectively use the provider model.  I wrote the solution in Vb.Net and have included it in the attached zip file.

 

Advance disclaimer: When I started into this, I thought to myself “of course you can do this without any of the System.Web classes” and, in fact, you totally can with just a little bit more work than I put into this. I cheated a bit by dropping in a reference to System.Web in order to gain access to System.Web.Configuration.ProvidersHelper to deserialize my provider elements, but this is only because I am lazy and was copying code from another project. That class is doing nothing fancier than standard deserialization of XML into an instance of a class.  Besides: Who says you can’t reference System.web from a Winforms app?

 

So, consider this scenario:

 

You develop a Winforms Application and have some standard functionality, business rules, etc. For example, your application calls a method that returns a standard report. Knowing that down the road, each one of your customers is going to want to change the way that report works, you decide to build the reporting class using the provider model.  This way, you can later build a custom DLL that implements the same interface as the reporting class (but with unique functionality) and can be installed by simply copying the DLL to the application folder and then making a change to the application configuration file (myapp.exe.config.)  Every customer can get his/her own unique reporting DLL and the main application never needs to be modified.

 

In a nutshell, this is how you do it for an existing class:

 

Step 1: Create a base “mustInherit” class which has the methods (but no code implementation) of your current class. In the COM world, this is analogous to an Interface.  Now, modify your class to inherit from this new base class.

 

Step 2: Create classes that will be used to deserialize information in your app.exe.config.  These will derive from classes in the System.Configuration namespace and will include one for your configuration section, one for a collection of provider settings and one for the provider settings itself.  Doing this will result in a strongly typed system for referring to your list of configured providers and their respective settings at runtime.

 

Step 3: Modify your main application so that rather than instantiating that existing class as you might have done before, you instead ask the configuration manager to use the information in yourapp.exe.config to load up your custom section and give you back an instance of the provider which has been dubbed as “the one to use.”

 

 

To illustrate this process, I have whipped up a very simple base provider “MyProviderBase” that exposes a single method:GetProviderSpecificMessage()”. I then have two separate implementations: “MyFirstProvider” and “MySecondProvider”, each of which handles GetProviderSpecificMessage() in its own way.  (While all of this is pretty basic stuff, the interesting part is where you enable selective usage of one or the other implementations through settings in the configuration file.) A tiny winform application, consisting of a button on a form does nothing more than load the appropriate provider and call its “GetProviderSpecificMessage” method.

 

With the configuration file set up to use the first provider, the output is as shown in the following message box. (Notice that defaultProvider=”MyFirstProvider” in the application configuration file shown below.)

 

 

 

Big note: In your solution, the file called app.config will be deployed as yourapplicationname.exe.config when you build.  When you distribute the application, just drop this config file into the same folder as the exe.

 

 

Now, do nothing more than change the configuration file to set defaultProvider=”MySecondProvider” and you get a totally different result when you run the program.

 

 

 

 

For a top-down look at the implementation, I will first talk about the winForm app (which I wrote last) and then about the provider implementation.  As you can see, the solution is very basic.

 

 

The WinForm Application:

Start a new Winform project and drop in a button control. In the button’s click event, get the implemented provider, call its GetProviderSpecificMessage method, and display its result in a MessageBox.  Here is the code:

 

Imports QualityData

Imports System.Configuration

Imports System.Web.Configuration

 

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim provider As MyProviderBase = GetProvider()

        If Not IsNothing(provider) Then MsgBox(provider.GetProviderSpecificMessage)

    End Sub

 

    Private Function GetProvider() As MyProviderBase

        ' get reference to configuration section for our provider

        Dim section As MyProviderSection = CType(ConfigurationManager.GetSection("myProvider"), MyProviderSection)

        If Not IsNothing(section) Then

            ' load registered providers and get reference to default provider

            Dim providers As New MyProviderCollection

            System.Web.Configuration.ProvidersHelper.InstantiateProviders(section.Providers, providers, GetType(MyProviderBase))

            Return CType(providers(section.DefaultProvider), MyProviderBase)

 

        Else

            Return Nothing

        End If

 

    End Function

 

End Class

 

 

Another big note: This is the place where I used the shared ProvidersHelper.InstantiateProviders method in the System.Web.Configuration namespace to walk through my section and instantiate each of the listed providers. I don’t know why Microsoft did not put the ProvidersHelper class into the System.Configuration assembly but it is really not such a big deal to have to get to it in System.Web.  If I get around to it, I might go for extra credit and write my own InstantiateProviders method by enumerating the elements and instantiating each provider based on its type.

 

 

The Application .config file

 

Here is the application’s config file (mine is called WinformProviderSample.exe.config.)

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <!-- ConfigSections section MUST be first section in app.config -->

  <configSections>

    <section name="myProvider" type="QualityData.MyProviderSection,MyProvider"/>

  </configSections>

 

  <!-- This is the custom provider section which gets loaded into the QualityData.MyProviderSection Class -->

  <myProvider defaultProvider="MySecondProvider">  <!-- Change this to swap providers -->

    <providers>

      <clear/>

      <!-- Here are one or more providers, each defined by its type "Class Name, DLL Name"

          and each with settings.  Within the class, MyProviderSection, these get loaded into

          and are accessed by "Providers" property -->

      <add name="MyFirstProvider"

           type="QualityData.MyFirstProvider,MyProvider"

           TheCustomSetting="This is the first provider setting value in app.config" />

 

      <add name="MySecondProvider"

           type="QualityData.MySecondProvider,MyProvider"

           TheCustomSetting="This is the second provider's setting value in app.config"

           TheExtraMessage="..And here is some extra information to configure the second provider!!!!" />

 

    </providers>

  </myProvider>

</configuration>

 

 

Notice that I have listed two providers: MyFirstProvider and MySecondProvider. As illustrated, the configuration file is set up to use the second.

 

Each of the provider elements has a name as well as a type attribute. The latter is structured as “ClassName,Assembly” where “ClassName” (QualityData.MyFirstProvider) is the fully qualified class name including the namespace and “Assembly” is the name of the assembly (MyProvider.dll) without the “.dll”.  You can tell that both classes are contained in the same assembly in this example.

 

Notice also that each provider section can have its own set of attributes. In “MySecondProvider”, for example, the attribute “TheExtraMessage” is optionally used to pass in extra configuration information that may be used by this particular implementation.

 

Hopefully you will agree that this is a fairly convenient and simple deployment story.

 

 

The Provider Classes

 

Now let’s talk about the provider. The project: MyProvider consists of several classes which I put into a single file for brevity.  Here is the code in its entirety:

 

Imports System.Configuration

Imports system.Configuration.Provider

Namespace QualityData

    ' This is the base class that defines the required methods of any implementation

    Public MustInherit Class MyProviderBase : Inherits ProviderBase

        ' For simplicity, we will have a single method that gets implemented

        ' differently based on the chosen provider.

        Public MustOverride Function GetProviderSpecificMessage() As String

 

    End Class

 

 

    ' The following two classes are "providers" that implement the base in different ways

    ' Ordinarily, each of these two classes would be in its own DLL since much of the idea of the

    ' Provider model is to be able to ship new behavior that can be "dropped in" without replacing

    ' the rest of the application

    Public Class myFirstProvider : Inherits MyProviderBase

        Private _myUniqueMessage As String

        Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)

            ' Override the method of Configuration.Provider.ProviderBase

 

            ' default provider name if missing

            If String.IsNullOrEmpty(name) Then name = "MyFirstProvider"

 

            ' initialize base and get custom settings

            If Not IsNothing(config) Then

                MyBase.Initialize(name, config)

 

                ' get the setting from config file

                _myUniqueMessage = config("TheCustomSetting")

 

            End If

 

            ' default setting to something uniquely from this provider

            If String.IsNullOrEmpty(_myUniqueMessage) Then _myUniqueMessage = "FIRST PROVIDER WAS HERE!!"

 

 

 

        End Sub

        Public Overrides Function GetProviderSpecificMessage() As String

            ' return string that has provider-specific prefix as well as data from config

            Return "The First Provider says: " & _myUniqueMessage

 

        End Function

    End Class

 

    Public Class mySecondProvider : Inherits MyProviderBase

        Private _myUniqueMessage As String

        Private _anExtraMessage As String

        Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)

            ' Override the method of Configuration.Provider.ProviderBase

 

            ' default provider name if missing

            If String.IsNullOrEmpty(name) Then name = "MySecondProvider"

 

            ' initialize base and get custom settings

            If Not IsNothing(config) Then

                MyBase.Initialize(name, config)

 

                ' get the setting from config file

                _myUniqueMessage = config("TheCustomSetting")

 

                ' this second provider can have its own extra settings

                _anExtraMessage = config("TheExtraMessage")

 

            End If

 

            ' default setting to something uniquely from this provider

            If String.IsNullOrEmpty(_myUniqueMessage) Then _myUniqueMessage = "#2 PROVIDER WAS HERE!!"

 

 

 

        End Sub

        Public Overrides Function GetProviderSpecificMessage() As String

            ' build up a return string that is specialized for this provider

            Dim sb As New System.Text.StringBuilder

            sb.Append("The Second provider does it differently: ")

            sb.Append(_myUniqueMessage)

            If Not String.IsNullOrEmpty(_anExtraMessage) Then sb.Append(" " & _anExtraMessage)

            Return sb.ToString

 

        End Function

    End Class

 

    ' We will be swapping out providers through simple changes to the app's .config file

    ' This can go quite deep to give you a lot more control but here are the basics:

    ' 1) MyProviderSection, defines a configuration section for app.config

    ' 2) MyProviderCollection, defines the collection of providers listed within our section of .config

 

 

    ' This class lets the configuration manager create a strongly-typed instance of our

    ' section based on the information listed in the application's .config file.

 

    Public Class MyProviderSection : Inherits ConfigurationSection

        <ConfigurationProperty("providers")> _

        Public ReadOnly Property Providers() As ProviderSettingsCollection

            Get

                Return CType(Me("providers"), ProviderSettingsCollection)

 

            End Get

        End Property

 

        <StringValidator(minLength:=1), ConfigurationProperty("defaultProvider", defaultvalue:="MyFirstProvider")> _

        Public Property DefaultProvider() As String

            Get

                Return CStr(Me("defaultProvider"))

 

            End Get

            Set(ByVal value As String)

                Me("defaultProvider") = value

 

            End Set

        End Property

    End Class

 

    ' This class lets the configuration manager create a strongly-typed collection of

    ' providers based on the information listed within the section for our custom provider.

    Public Class MyProviderCollection : Inherits ProviderCollection

        Public ReadOnly Property MyProviderBase(ByVal name As String) As MyProviderBase

            Get

                Dim o As Object = Me(name)

                If o IsNot Nothing Then Return CType(o, MyProviderBase) Else Return Nothing

 

            End Get

        End Property

        Public Overrides Sub Add(ByVal provider As System.Configuration.Provider.ProviderBase)

            ' check for valid type (ie, make sure the listed provider inherits from our base)

            If IsNothing(provider) Then Throw New ArgumentNullException("provider")

            If Not TypeOf provider Is MyProviderBase Then Throw New ArgumentException("Invalid provider type", "provider")

 

            ' add

            MyBase.Add(provider)

 

        End Sub

    End Class

End Namespace

 

 

Most of this is self explanatory and hopefully the code comments will help to make it clear. Notice that we have a (mustinherit) base class that inherits System.Configuration.Provider.ProviderBase and defines the single required method “GetProviderSpecificMessage”.  We have two classes that inherit from that base and override the Initialize method to load in the configuration goodies that are passed in as a NameValueCollection.

 

We also have a custom ConfigurationSection class that minimally extends its base by setting the defaultProvider’s defaultValue property to “MyFirstProvider”. 

 

Finally, we implement “MyProviderCollection” which inherits from System.Configuration.ProviderCollection and contains a list of providers that derive from our custom provider base (MyProviderBase.)

 

Download, run and extend the source in the project and let me know if any of this is useful to you.

 

 

 

Brian Mishler

WinformProviderSample.zip (99.86 KB)
Saturday, February 03, 2007 10:51:37 PM (Eastern Standard Time, UTC-05:00)  #    Comments [2] -
.Net | Asp.Net Providers
Navigation
Archive
<February 2007>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728123
45678910
Blogroll
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Brian Mishler
Sign In
Statistics
Total Posts: 13
This Year: 0
This Month: 0
This Week: 0
Comments: 15
Themes
Pick a theme:
All Content © 2010, Brian Mishler
DasBlog theme 'Business' created by Christoph De Baene (delarou)