Keyvan Nayyeri released his
OPML-to-Blogroll Converter Add-on for CS 2.0 a few days ago and I jumped on it. I installed it on my CS 2.0 development server with little problem, as Keyvan's instructions were brief and clearly written. I had to tweak the exception handling a bit to process my GreatNews Reader OPML file all the way through without spitting out a runtime, but it wasn't a problem.
But the thing I couldn't deal with was how long it took to convert my @120 feed OPML file to a CS Links list. The converter cranked for well over a minute. That wouldn't do, and I had to get to the bottom of what was going on.
Keyvan's converter does a lot of XML handling, using XSL Transform on the OPML file as well as on the temporarily created XML files for LinkCategories and Links from the OPML file. First thing I did was rework the logic and use a straight XMLTextReader, grabbing elements and attributes as needed. My GreatNews OPML file is two-tiered (as any resulting CS Link List would be), so I based XML handling on what tier the OUTLINE element was located; a category had a reader.depth of 2, a link reader.depth 3.
I compiled up the XML handling changes from Keyvan's approach, but conversion was STILL cranking for over a minute to do the job. It wasn't XML handling sucking up the cycles, it was the native CS WeblogDataProvider CreateLinks() method being used for each and every OPML feed entry entered into CS. CHOKE! Not a criticism of Keyvan or of CS, the CS WeblogDataProvider was never designed to enter multiple links like this into the CS_Links table.
Using the CS DataProviders is a very clean "best practice" type of implementation leveraging the CS architectural model, but another approach was needed to make this app useable.
The DataProvider approach:
private void AddLinks(DataSet Sites, System.Collections.Specialized.StringDictionary CategoryDic)
{
foreach (DataRow Row in Sites.Tables[0].Rows)
{
CommunityServer.Components.Link objLink = new Link();
objLink.Title = Convert.ToString(Row["Name"]);
objLink.Description = Convert.ToString(Row["Description"]);
objLink.Url = Convert.ToString(Row["Url"]);
objLink.IsEnabled = true;
objLink.LinkCategoryID = Convert.ToInt32(CategoryDic[Convert.ToString(Row["Category"])]);
Links.CreateLink(objLink);
}// foreach
}// AddLinks
My idea was to read through the OPML, enter a LinkCategory, then enter all of its links with a single batch statement. So the core logic I decided to go with was:
private void EnterLinkItem(XmlTextReader _reader)
{
string _title = QuoteClean(_reader.GetAttribute("title"));
string _desc = QuoteClean(_reader.GetAttribute("description"));
string _url = _reader.GetAttribute("htmlUrl");
int _settingsID = CSContext.Current.SettingsID;
_linkSqlStatement += "insert into cs_links values (" + _linkCategoryID + ",'" + _title + "','" + _url + "'," +
"1," + _linkSortValue + "," + _settingsID + ",null,'" + _desc + "','" + DateTime.Now + "');\n";
_linkSortValue++;
}
private void ExecuteLinkSQLStatement() // called when Link Category Changes
{
SQLHelper.ExecuteNonQuery(_linkSqlStatement);
_linkSqlStatement = string.Empty;
}
The complete conversion class is
viewable here. I did use the CS DataProvider for entering the Link Categories. Hey, I LIKE the CS DataProvider model! I could have squeezed out even better performance if I did something similar with Link Category entry, but my obsession with this conversion process paid off. The original 60-90 seconds processing time was reduced to less than 5 seconds.
I'll have the source available when my OPML Blogroll is online. I'm still deciding on how to display it. One of the things I'll need to do is to add expanding panels for the OPML categories, so that will be part of the mod as well. I'll keep you posted.
Thanks for allowing me to piggyback on your excellent work, Keyvan!
[tags: Community Server, OPML, Blogroll]