There are many uses for providers with accessing a data store being one of the most common. I'm not going to talk about why to use providers, I am going to address how to use generics and interfaces with providers to make using them a bit easier.
For this series we are going to use a "ContactInformation" object with a few sub-classes, "HomePhoneNumber" and "EmailAddress".
using System; using System.Collections.Generic; using System.Text; using ObjectHelpDesk.DataAccess; using System.Security.Principal;
public abstract class PhoneNumber : ContactInformation { private string _number; public string Number { get { return _number; } set { _number = value; } } } public class HomePhoneNumber : PhoneNumber { public HomePhoneNumber() { Name = "Home Phone"; } } public class EmailAddress : ContactInformation { public EmailAddress() { Name = "Email Address"; } private string _address; public string Address { get { return _address; } set { _address = value; } } }
public abstract class ContactInformation : IReadDataObject<ContactInformation,Int32> { private String _name; public String Name { get { return _name; } set { _name = value; } } }
One thing I have noticed is that most providers use the same basic Configuration Section, the section has a list of providers, the default provider and nothing else; so let's look at that code first.
using System.Configuration; namespace ObjectHelpDesk.DataAccess { public class ProviderConfigurationSection : ConfigurationSection { [ConfigurationProperty("providers")] public ProviderSettingsCollection Providers { get { return (ProviderSettingsCollection)base["providers"]; } } [ConfigurationProperty("default", DefaultValue = "default")] public string Default { get { return (string)base["default"]; } set { base["default"] = value; } } } }
In the web.config you would tie the code to a section by adding a line to the configSections and create the section as well.
<configuration> <configSections> ... <section name="ContactInformationProvider" type ="ObjectHelpDesk.DataAccess.ProviderConfigurationSection"/> </configSections> ... <ContactInformationProvider default="dummy"> <providers> <add name="dummy" type="DummyProviders.dummyContactInformationProvider,DummyProviders"/> </providers> </ContactInformationProvider>
Where does the ContactInformationProvider section name come from? it is set by ...<section name="ContactInformationProvider"... But that is only part of it, there is another part in the code; we will get to that a little later.
Next we have two classes, ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType> and ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType, ProviderConfigurationSectionType>.
ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType> is defined as follows
public class ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType> : ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType, ProviderConfigurationSection> where ProviderCollectionType : ProviderCollection, new() where ProviderType : ProviderBase { public ProvidersRepositoryGeneric() : base() { } }
The class has very little to it, in fact it inherits the other class ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType, ProviderConfigurationSectionType>. Why did I do this? I do not expect to use a different ProviderConfigurationSection class very often and I would rather only have to set two types in my generics. There are couple of things that are interesting about this class...
- The constructor calls the base classes constructor. The base class is also generic. I have seen discussions about it being required to pass generic types in the constructor; it is not true.
- The where clause, if you are not familiar with generics and the where conditions you might want to take a few minutes and look them up. The short version is "where ProviderCollectionType : ProviderCollection, new() " means that whatever type is passed in as ProviderCollectionType must be a sub class of the ProviderCollection class and it must have a constructor that takes no parameters. "where ProviderType : ProviderBase " means whatever type is passed in as the ProviderType must be a sub class of the ProviderBase class. Both the ProviderCollection and ProviderBase are part of the .net framework (and are used in this series)
- The class name is identical as the parent class but the number of generics is different so they are not seen as the same class. (overloading a class?? oh my...)
The other class ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType, ProviderConfigurationSectionType> has a bit more to it...
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration.Provider; using System.Configuration; using System.Web.Configuration; namespace ObjectHelpDesk.DataAccess.Generics { public class ProvidersRepositoryGeneric<ProviderCollectionType, ProviderType, ProviderConfigurationSectionType> where ProviderCollectionType : ProviderCollection,new() where ProviderType : ProviderBase where ProviderConfigurationSectionType : ProviderConfigurationSection { public ProvidersRepositoryGeneric() { LoadProviders(); } private ProviderCollectionType _providers = null; private ProviderType _provider = null; private void LoadProviders() { //if the provider is set someone beat us here and bail. if (_provider == null) { //make an object to lock the method/objects with. Object processLock = new Object(); lock (processLock) { //ok someone else could have been waiting and hit this once the lock cleared //so lets make sure the provider hasnt already been set and skip this if it has. if (_provider == null) { ProviderConfigurationSectionType configuration = (ProviderConfigurationSectionType) ConfigurationManager.GetSection(SectionName); if (configuration == null) throw new ConfigurationErrorsException (SectionName + " configuration section is not set correctly."); _providers = new ProviderCollectionType(); ProvidersHelper.InstantiateProviders(configuration.Providers , _providers, typeof(ProviderType)); _providers.SetReadOnly(); _provider = (ProviderType)_providers[configuration.Default]; if (_provider == null) throw new ProviderException("The Default Provider was not configured");
ReadCustomConfigurationSettings(configuration); }//if _provider null inner check }//end of lock }//_if provider null }
protected virtual void ReadCustomConfigurationSettings(ProviderConfigurationSectionType configuration) { }
public ProviderCollectionType Providers { get { if (_providers == null) LoadProviders(); return _providers; } } public ProviderType Provider { get { if (_provider == null) LoadProviders(); return _provider; } } private String _sectionName = ""; private string SectionName { get { if (String.IsNullOrEmpty(_sectionName)) _sectionName = typeof(ProviderType).Name; return _sectionName; } } } }
Right away we see the where clauses on the generics are the same except there is a new one "where ProviderConfigurationSectionType : ProviderConfigurationSection". This restricts the type passed in as ProviderConfigurationSectionType to the class ProviderConfigurationSection and/or it's sub-classes.
The class has two methods, LoadProviders, which is called from the constructor, and ReadCustomConfigurationSettings, which is called by LoadProviders .
We have several fields that have public accessors. Each accessor is checking the field for a value and if it is not set it is calling LoadProviders or setting the field. Why call LoadProviders when it is called in the constructor? Catastrophic memory failure, or worse forced garbage collection from low resources (In theory they should not be picked up by GC without the containing object being nuked.) Notice the private property SectionName? There is no real reason that it is private, but it is that "other part in code" that determines the section name in the web.config. If you switch it to public add a set accessor you and remove the LoadProviders call from the constructor you can change the section name you want to read from in code.
The LoadProviders method reads the providers out of the config section, if I was going use a custom ProviderConfigurationSection I would make a subclass of the class and override the ReadCustomConfigurationSettings method to read the custom settings. Simply put, LoadProviders creates a collection of Providers, reads the configuration section and creates the providers. The line "_providers = new ProviderCollectionType();" creates the empty collection, it is also the reason for the new() where clause in the class declaration. These classes can be used for a nice little provider model without interfaces.
With 4 interfaces we can have a nice Provider model for CRUD (Create, Read, Update, Delete). However, you can also break that into four sets of interfaces allowing a great deal of flexibility. If create, read, update and delete each had their own interface you could implement Create and read to make a log system that you can read from and create new entries but not update or delete. In most cases that kind flexibility might be like using a sledge hammer to put a push pin in the wall...
Next : Generics, Interfaces, Providers and You - Part 2:IDataObject & IProvider