A couple of years ago at Teched South Africa I presented a session originally done by the great Jeff Prosise around the provider model in ASP.Net.  I remember suggesting that this was not only a neat way to handle membership, roles and whatever in ASP.Net but was a great architectural pattern that you should use when you can.

Well a couple of weeks ago Hans came up with the idea of our team building an application and demoing it to our extended team when we meet later this year.  The app is to be an Org chart browser.  Carrie will be in charge of the cool graphics WPF/Silverlight, and I will handle the heavy metal of the program.  Of course I have chosen a provider model to access org charts and present them in a canonical form to the developer/Blend designer.

So to recap, the membership provider model works like this – you program against static members of the Membership class to get instances of MembershipUser classes.  These latter classes represent the individual users and their accounts.  A typical call would be:

MemberhshipUser user = Membership.GetUserByEmail(“fred.witkins@mycompany.com”);

The plumbing beneath all of this is using a MembershipProvider class that is specified in a config file.  The Membership static methods are loading this dll and making calls into it to get and modify instances of the MembershipUser class. 

So there are three main classes that make this work, Membership, MembershipUser and for those writing providers a subclass of MembershipProvider.

 

Lets do this with the Org chart:  I will use OrgChart, OrgPerson and OrgChartProvider classes.

 

The key class is the OrgChart that will have static methods that return OrgPerson objects.  Thus:

OrgPerson person = OrgChart.GetPersonByEmail(“fred.witkins@mycompany.com”);

An OrgPerson is analgous to a User in the Membership case but this time there is the added wrinkle that OrgPersons are related to one another in ways that Users are not – we all have managers (except for one of us) and some of us are privileged to  have direct reports.  So what is the most simple way to navigate the tree of OrgPersons?  Well here is my first stab at a canonical schema for OrgPerson

public class OrgPerson

    {

 

 

        private OrgChartProvider _provider;

        /// <summary>

        /// bit to state if this OrgChartPerson has been accessed.  Use for lazy evaluation of org trees

        /// </summary>

        private Boolean _hasBeenTraversed;

 

        private string _firstName;

 

        /// <summary>

        /// Properties use accessors to appear in Blend and other databinding tools

        /// </summary>

        ///

        private string _salutation;// mr mrs etc

 

        public string Salutation

        {

            get { return _salutation; }

            set { _salutation = value; }

        }

        private string _title;

 

        public string Title

        {

            get { return _title; }

            set { _title = value; }

        }

 

 

 

        public string FirstName

        {

            get { return _firstName; }

            set { _firstName = value; }

        }

        private string _lastName;

 

        public string LastName

        {

            get { return _lastName; }

            set { _lastName = value; }

        }

....

 

 

You get the idea.  I also add a property that accesses the direct reports of the person but here is the rub, I want to be able to have the OrgChartProvider writer optimize the traversal of the tree but to keep it intuitive and simple to the app programmer.  Of course its up to the provider writer to choose to do this or not but I decided to add a little hook to help him do that:

private List<OrgPerson> _directReports;

 

        public List<OrgPerson> DirectReports

        {

            get

            {

                if (!_directsTraversed && _provider != null)

                {

 

                    this._directReports = _provider.FindDirectReports(this);

                    _directsTraversed = true;

                    return _directReports;

                }

            }

            set { }

        }

I keep a Boolean _directsTraversed so that the OrgChartPerson can call into the provider to get more chunks of data when it is needed.  I will later use a config section to specify private settings for the provider so that it can restrict an entire tree traversal for a simple query. This Boolean value will be set by the provider and can default to true if the entire tree is evaluated in one go.

We can handle the traversal the other way in the same manner to find a person’s manager.

private List<OrgPerson> _manager;

 

        public List<OrgPerson> Manager

        {

            get

            {

                if (!_mangerTraversed && _provider != null)

                {

 

                    this._manager = _provider.FindManager(this);

                    _mangerTraversed = true;

                }

return _directReports; // can be null if there are no directs

 

            }

            set {}

        }

 

 

This is a classic pattern to give a lazy evaluation of the manager object. In fact it is the _provider member that is doing the hard work but the programmer just sees the OrgPerson as a simple object.  So it looks like we have a two way navigation built into the tree but the magic is done by the OrgChartProvider class.  So lets look next at how that class might work.

I will again borrow the same pattern as the provider model in ASP.Net.   I will have an abstract base class:

public class OrgChartProvider

    {

 

...

        public abstract List<OrgPerson> FindDirectReports(

        string managerNameToMatchRegex,

 

        out   FindPersonStatus status);

 

 

        public enum FindPersonStatus { success, invalidName, ambiguousExpression, notImplemented };

 

       

        public abstract List<OrgPerson> FindDirectReports(

    OrgPerson manager,

 

    out   FindPersonStatus status);

 

 

        public enum FindPersonStatus { success, invalidName, ambiguousExpression, notImplemented };

 

        public abstract List<OrgPerson> FindPerson(

       OrgPerson personToMatch,

 

       out   FindPersonStatus status);

        public abstract System.Collections.Generic.List<OrgPerson> FindPerson(

            string personToMatchRegExp,

 

            out   FindPersonStatus status);

 

    };

 

 

So my implementation will fill out these abstract methods.

So I now have identified the triad of classes that make the basic provider model work.  Next I will fill out these stubs.