Community Server Source Code – Abstract Classes, Reflection and Data Providers

In my post, Data Access Application Block Revealed – Factory Methods and Reflection, I talked about how the Enterprise Library Data Access Application Block used abstract classes and reflection to instantiate data providers to provide a extensible solution for data storage.  As luck would have it, Community Server also exploits the concepts of abstract classes, reflection, and data providers to pull off a similar architecture for their data storage.  To reinforce these concepts, let’s dig into the Community Server source code to show how a post is viewed on a blog.


View-Post.ascx is the user control responsbile for displaying a post, and it basically just hands the work off to the class, EntryViewContainer.  Here is a snippet of EntryViewContainer:


 


EntryViewContainer
public class EntryViewContainer :  WeblogThemedControl
{
//

void BindData()
{
CSContext csContext
= CSContext.Current;

BlogPostQuery query = new BlogPostQuery();
query.PostID
= csContext.PostID;

string name = Context.Request.QueryString[PostName];
if(!Globals.IsNullorEmpty(name))
query.Name
= name;

query.IncludeCategories = false;
query.ReturnFullThread
= true;
query.BlogID
= CurrentWeblog.SectionID;

PostSet ps = WeblogPosts.GetPosts(query,true);

//
}


 


One of the interesting things here that I won’t really go into is the use of a BlogPostQuery object to pass parameters that essentially end up as parameters for stored procedures.  This is a nice way to package up parameters as opposed to having several overloads of GetPosts that take varying parameters.


As you can see, EntryViewContainer just grabs the BlogID and PostID along with a few defaults and calls WeblogPosts.GetPosts to go get the post to be displayed.  It actually returns a PostSet object, which is basically a thread of posts – the main post and the flow of comments to the post.


WeblogPosts is essentially just a set of functions that handle post related activity.  You could call it a Controller class of sorts as it is the first object beyond the UI layer that is responsible for receiving or handling a system operation message (See Applying UML and Patterns: Controller GRASP Pattern – Model View Controller Design Pattern – First Object Beyond UI Layer).


 


WeblogPosts Class
public class WeblogPosts
{
private WeblogPosts(){}

//

public static PostSet GetPosts(BlogPostQuery query, bool cacheable)
{
string key = cacheable ? query.Key : null;
PostSet ps
= null;

if(cacheable)
ps
= CSCache.Get(key) as PostSet;

if(ps == null)
{
WeblogDataProvider wdp
= WeblogDataProvider.Instance();
ps
= wdp.GetPosts(query);

if(cacheable)
CSCache.Insert(key,ps,
30,System.Web.Caching.CacheItemPriority.Low);
}

return ps;
}

//
}


 


Here is where the caching is done as well as where we start to see the data providers coming into action.  WeblogDataProvider is an abstract class with not much more than a single method, Instance(), that grabs the concrete data provider class that handles the storage operations for blogs.  The concrete class returned is the class that actually carries out the operation to get the post and its comments from data storage.  Here is some of the code in WeblogDataProvider:


 


WeblogDataProvider Class
public abstract class WeblogDataProvider
{
public static readonly string WeblogDataProviderName = WeblogDataProvider;

//

private static WeblogDataProvider _defaultInstance = null;

static WeblogDataProvider()
{
CreateDefaultCommonProvider();
}

public static WeblogDataProvider Instance()
{
return _defaultInstance;
}

private static void CreateDefaultCommonProvider()
{

CSConfiguration config = CSConfiguration.GetConfig();

Provider sqlForumsProvider = (Provider) config.Providers[WeblogDataProviderName];

_defaultInstance = DataProviders.CreateInstance(sqlForumsProvider) as WeblogDataProvider;
}

//
}


 


The main work happens in the CreateDefaultCommonProvider method.  It goes out and grabs the configuration information which contains the name of the concrete class that will be handling the data storage and retrieval of blog posts.  This information is tucked away in a config file, called communityserver.config, that has the following entry:


 


communityserver.config
<add
name
= WeblogDataProvider
type
= CommunityServer.Data.WeblogSqlDataProvider,CommunityServer.SqlDataProvider
connectionStringName
= SiteSqlServer databaseOwnerStringName = SiteSqlServerOwner
/>

 


There is the concrete class that will actually be handling the blog storage and retrieval: WeblogSqlDataProvider.  You can guess that we will need to dynamically create this class during runtime.  This is the job of the DataProviders class, which is just a set of helper functions for just that thing:


 


DataProviders Class
public sealed class DataProviders
{

private DataProviders()
{
}

//

public static object CreateInstance(Provider dataProvider)
{
string connectionString = null;
string databaseOwner = null;

GetDataStoreParameters(dataProvider, out connectionString, out databaseOwner);

Type type = Type.GetType(dataProvider.Type);

object newObject = null;

if(type != null)
{
newObject
= Activator.CreateInstance(type,new object[]{databaseOwner,connectionString});
}

if(newObject == null)
ProviderException(dataProvider.Name);

return newObject;
}

//
}


 


The CreateInstance method is the work horse that uses reflection to create an instance of the concrete class, WeblogSqlDataProvider, that will eventually get returned to the controller class, WeblogPosts.


As you can see below, WeblogSqlDataProvider is making use of the BlogPostQuery object to pass out the values for the parameters and essentially just calling the stored procedure that returns a few resultsets of data to be passed back as a PostSet.


 


WeblogSqlDataProvider
public  class WeblogSqlDataProvider : WeblogDataProvider
{

//

public override PostSet GetPosts(BlogPostQuery query)
{
using( SqlConnection connection = GetSqlConnection() )
{
using(SqlCommand command = new SqlCommand(databaseOwner + .cs_weblog_Postset, connection))
{
command.CommandType
= CommandType.StoredProcedure;

command.Parameters.Add(@SectionID, SqlDbType.Int).Value = query.BlogID;
command.Parameters.Add(
@PostID, SqlDbType.Int).Value = query.PostID;
command.Parameters.Add(
@PostName, SqlDbType.NVarChar).Value = query.Name;
command.Parameters.Add(
@PageIndex, SqlDbType.Int, 4).Value = query.PageIndex;
command.Parameters.Add(
@PageSize, SqlDbType.Int, 4).Value = query.PageSize;
command.Parameters.Add(
@ReturnFullThread, SqlDbType.Bit, 1).Value = query.ReturnFullThread;
command.Parameters.Add(
@UserID, SqlDbType.Int).Value = query.UserID;
command.Parameters.Add(
@IncludeCategories, SqlDbType.Bit).Value = query.IncludeCategories;
command.Parameters.Add(
@TotalRecords, SqlDbType.Int).Direction = ParameterDirection.Output;

PostSet ps = new PostSet();

connection.Open();

//NOTE TO SELF: We return the results as seperate sets so that we can avoid some of the more
//expensive lookups done ont the main posts!

using(SqlDataReader reader = command.ExecuteReader())
{
//this will return just one post, forget the rest if nothing found here
if(reader.Read())
{
WeblogPost entry
= new WeblogPost();
PopulateWeblogEntryFromIDataReader(reader,entry);

if(query.IncludeCategories)
{
reader.NextResult();
ArrayList categories
= new ArrayList();
while(reader.Read())
{
categories.Add(reader[
Name] as string);
}

entry.Categories = (string[])categories.ToArray(typeof(string));
}

//we could be setting the thread starter right here?
ps.Posts.Add(entry);

//comment list
reader.NextResult();

while(reader.Read())
{
entry
= new WeblogPost();
PopulateWeblogEntryFromIDataReader(reader,entry);

ps.Posts.Add(entry);
}

reader.NextResult();
ps.TotalRecords = (int) command.Parameters[@TotalRecords].Value;

reader.Close();
}
else
{
reader.Close();
}
}

return ps;

}
}
}

//

}

 

 


When you break down the source code like this it becomes pretty clear how all these fundamental OOP techniques come into play .  There are a few other goodies in Community Server that are probably worth talking about at a later time, too.

This entry was posted in C#, GRASP Patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to Community Server Source Code – Abstract Classes, Reflection and Data Providers

  1. Liang says:

    Very appreciate, David! It is very interesting to look at the source code. The first time I saw this Provider Pattern was from Rainbowportal.

  2. dhayden says:

    Here is the link to download the community server source code for version 1.0:

    http://www.telligentsystems.com/Solutions/license.aspx?File=cs_1.0_src.exe

  3. Liang says:

    That is very nice, David!
    Quick question: how do you get the source code? I downloaded the project as well from their site. But only ASPX files and DLLs, there has no source code for those DLLs.

  4. Jeff Perrin says:

    “With a traditional AF pattern you wouldn’t need the Activator.CreateInstance stuff, which is does have performance issues.”

    Since it looks like the instance will only ever be created once, this isn’t really an issue.

  5. dhayden says:

    Hey Brendan,

    There is no abstract factory pattern used in the above example.

    In this piece of code:

    WeblogDataProvider wdp = WeblogDataProvider.Instance();

    the Instance() method is often referred to as a Factory Method. It essentially hides the actual class that is being instantiated from the client code.

    There is no requirement that the concrete class being created is done via reflection or not.

    Enterprise Library and Community Server use reflection only to support instantiating classes in 3rd party assemblies. This creates more of a true plug-in framework.

    The Abstract Factory Pattern has to do with creating families of classes, which is not what is being done here.

    A good series of articles on design patterns would be a phenomenal contribution to the developer community. So many things to talk about, so little time :)

  6. I may be a little confused about Abstract Factory… I thought you could do it without the reflection stuff, but now that I think about it, (and read your first post) I see that this is standard.

  7. This is a nice explanation. I’ve not delved this deep into the actual data provider implementation yet, but I have a question for you..

    Is this a variation on the Abstract Factory pattern? If so, what is the benefit of doing it this way? With a traditional AF pattern you wouldn’t need the Activator.CreateInstance stuff, which is does have performance issues.

    I’m sure there are very good reasons for doing it this way – a “method to the madness.”

    Community server is a pretty large app.. there are all kinds of neat things to see once you get under the covers, huh?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>