Telerik SiteFinity - first impression

At the We Are Microsoft charity event last weekend, our team decided to implement a solution using a CMS product so they easily would be able to update the content. We decided to use Telerik SiteFinity based on the repution of Telerik and the fact that they had developer training the first day. Perhaps not the strongest reasons to pick a product but in our case we did not regret picking SiteFinity. The Web CMS Report have categorized the most well known CMS products, SiteFinity is categorized "Mid Market Challengers" which from what I can tell is an accurate categorization.

We used SiteFinity version 3.5 SP 1. The intial setup was a bit painful because the MSI installer didn't work too well on my Vista laptop but thankfully evengalist Gabe Sumner pointed out during the training that you can download a SiteFinity Visual Studio solution from sitefinity.com . The solution file is a Visual Studio Web Site template, I would have preferred a Web Application Project but I assume it's easy enough to change if you really want to change it.

SiteFinity plugs into the regular VS template pretty nicely. You will without a doubt want to add your own master page and the good news is that you just add it like you would in a normal ASP.NET web site. You need to add a script manager in the markup though like this:







You also need to add a theme to the App_Themes folder. This is where you put your css files and images that you use in the header/body/footer via your master page. Once you restart your worker process or the web server if you use Cassini, both the master page (template) and your theme should be visible once you edit a page. If you select a the template and the theme you created earlier, you should get the look and feel your expecting.





This is the basics to set it up, I will go into more detail after adding more functionality.

What I like about SiteFinity so far:

  • WSIWYI Editor for editing content.
  • Very easy for non-technical people to learn to use with a little training. Of course, you still need to know some basic HTML to make it look right.
  • Workflow enginge in the Standard version, which is nice to have. I haven't had a chance to use it yet though.
  • User controls hooks into SiteFinity in a nicely, allowing drag-and-drop in the GUI. You do have to add an entry into the web.config file though.
  • It's relatively inexpensive, I don't think $899 is much money for a commercial organization for a CMS product where you get support.
What I don't like:
  • All pages appear to use ViewState by default, whether it's static text only or a form. I think ViewState in general is way overused in ASP.NET web forms in general, I wish Telerik didn't fall into this trap as well but perhaps there's a good reason.
  • I really don't understand why even images must use an Httphandler ASHX but perhaps it's not as heavy handed as it seems to be.
  • Error handling seems a little sparse. Both during the setup and if it can't find the database when you run it, you get uncaught exceptions.
  • ASP.NET 3.5 has been out for a good while now, you expect products to use 3.5 instead of 2.0 by now.
All in all, I really like this product and I'm glad I got a chance to try it out. The road map for version 4 looks pretty interesting. I wish they would have mentioned using the ASP.NET MVC framework instead of the ASP.NET Web Forms though.

We-Are-Microsoft charity event - wrap-up

I just got back from the charity event We Are Microsoft hosted in Dallas. It was a wonderful event, so many charities that all were worthwhile causes. I met a bunch of wonderful people, it is amazing that so many people were willing to give up a full weekend away from their families. I think the final tally was over 170 people including developers, designers, charities and event staff. It was very exhausting weekend of coding and little sleep but great to see that all charities actually received a fully working web site. Chris Koenig and Toi Wright put together a really great event plus all the volunteers.

Our team built a web site for the shelter New Beginning Center using Telerik Sitefinity as the CMS of choice. The new website isn't quite ready yet but will hopefully be up in the next few weeks as we're working on the finishing touches.

None of the guys on our team had any prior experience using SiteFinity but thankfully it was relatively easy to use. Telerik also had two great developer evengalists, Gabe Sumner and Todd Anglin who really knew the product well and were great at providing feedback. I will post later this week what I liked and disliked about SiteFinity.

ASP.NET MVC - StoreFront

Like so many others in the .Net community, I've been looking at the ASP.NET MVC framework lately which as of this writing is currently in beta. I really like what I've seen so far but YMMV. It seems like many developers that have experience using MonoRail have raised quite a few concerns. Obviously, MonoRail has been around for quite a while and have a lot more features and some of the design decisions may make MonoRail more extensible than ASP.NET MVC ever will be.

I've used the User Interface Process (UIP) Application Block previously in some projects and ASP.NET MVC is quite impressive compared to UIP :-). I really like that you can replace the view engine and that the aspx pages are so much more lightweight compared to Web Forms.

One of the more fascinating sample applications that I have seen come from Microsoft as long as I can remember is the MVC StoreFront by Rob Conery. It is so refreshing to finally see a sample application that has some complexity. It takes quite a bit of time to go through all the video's but is well worth it where Rob goes through using ASP.NET MVC and concepts like Dependency Injection, TDD, Mocking and even Domain Driven Design. It's a little bit confusing that the first 23 video's are at ASP.NET / MVC while also all the previous and new episodes (25 and forward) with topics like Domain Driven Design is at Rob's blog. There are a ton of good comments at his blog so that's the best place to go to.

The MVC StoreFront is just a tremendous learning experience using a variety of frameworks that may be useful in your projects. Extra plus that Rob doesn't just "pimp" Microsoft solutions and educate the use of some OSS frameworks.

IE 8 Web Slices - part 2

See part 1 for the introduction of this web slice implementation where we set up the order web page. In the order page we showed where we set the RSS feed that we want to point to. In this part we will implement the Httphandler and RSS feed.

There's plenty of information available on how Httphandlers work, I won't go into the details about that. Our Httphandler listens for requests coming in to our SalesOrderWeb web site that takes the form http://localhost/SalesOrderWeb/123/OrderTracking.rss where "123" is the SalesOrderId of an order in the AdventureWorks database we want to look up.

The IHttpHandler interface have one method and one property that must be implemented:

  • IsReusable
  • ProcessRequest
The IsResuable property has confused many developers due to very sparse documentation about it. It can be set to true if multiple requests can process the HttpHandler at the same time, i.e. we don't have any private data or similar that could inadvertily be shared between two requests that run at the same time. Clear as mud? Good. Our HttpHandler does not have any data that is shared between requests so we can set IsResuable to true.

The ProcessRequest method will process the requests for us and output the processed data when we are done. This sequence diagram shows roughly the sequence of events that takes place.



The actual ProcessRequest method in our HttpHandler looks like this:


public void ProcessRequest(HttpContext context)
{

if (ValidateParameters(context) == false) // Not good
{
context.Response.StatusCode = 500;
context.Response.End();

return;
}

string[] requestPath = context.Request.Path.Split((char)'/');
string salesOrderNumber = requestPath[2];

FeedChannel chan = new FeedChannel(salesOrderNumber);
string feed = chan.GetRSSFeed();

context.Response.ContentType = "text/xml";
context.Response.Write(feed);
}


The first thing we need to do is to validate the input which is handled in the ValidateParameters method. In this sample we're just checking that we have what appears to be a SalesOrderId and then we get the salesOrderId out of the request.

Next, we instantiate the FeedChannel which loads the SalesHeader and SalesDetail for this sales order and we build the html that will be displayed in the "Description" field in the RSS item. In this implementation we only will have one single FeedItem but it's possible to have more than one FeedItem for every channel if you have aggregate data that you want to display in your feed.

We call the GetRSSFeed method which will create the XML we want to output and display in the web slice. We use the XmlDocument in .Net and the associated classes to help build the request in this case.


public string GetRSSFeed()
{
XmlElement workNode = null;
XmlElement itemNode = null;

// Get the custom section which contains the default values for the RSS feed.
SalesOrderConfigSection soSection = SalesOrderConfigSection.GetConfig();

XmlDocument doc = new XmlDocument();
XmlProcessingInstruction pi =
doc.CreateProcessingInstruction("xml-stylesheet",
string.Format("type='text/css' href='http://{0}/SalesOrderWeb/css/rss.css'"
, Dns.GetHostName()));
doc.AppendChild(pi);

XmlElement docNode = doc.CreateElement("rss");
XmlAttribute attribute = doc.CreateAttribute("version");
attribute.Value = "2.0";
docNode.Attributes.Append(attribute);
doc.AppendChild(docNode);

// Create the channel element and its subelements:
XmlElement channelElement = doc.CreateElement("channel");
docNode.AppendChild(channelElement);

AddElement(doc, channelElement, "title", string.Format(_title, SalesOrderId));
AddElement(doc, channelElement, "link", string.Format(soSection.URL,
Dns.GetHostName(), SalesOrderId));
AddElement(doc, channelElement, "description", soSection.Description);
AddElement(doc, channelElement, "ttl", soSection.ttl);

// Add the information about each line item.
foreach (FeedItem feedItem in _feedItems)
{
itemNode = doc.CreateElement("item");
channelElement.AppendChild(itemNode);
workNode = doc.CreateElement("title");
workNode.InnerText = string.Format(feedItem.Title, SalesOrderId);
itemNode.AppendChild(workNode);
workNode = doc.CreateElement("description");
workNode.InnerText = feedItem.Description;
itemNode.AppendChild(workNode);
workNode = doc.CreateElement("link");
workNode.InnerText = string.Format(soSection.URL, Dns.GetHostName(), SalesOrderId);
itemNode.AppendChild(workNode);
}

return doc.OuterXml;
}


The returned xml is written into the HttpResponse, formatted as text/xml and returned to the browser.


context.Response.ContentType = "text/xml";
context.Response.Write(feed);


The feed is a fairly normal RSS feed that can be viewed in the browser, in a separate RSS Reader or in the web slice as the intent was in this case.

IE 8 Web Slices - part 1

Recently I've been looking at IE 8 and the new features that have been added, web slices and accelerators. They're both useful, web slices in particular since it can be used in any kind of information you want to keep updated like a sales order for example. It also helps that Microsoft has released the features as an open specification, Firefox has an add-on called WebChunks instead of Web Slices.

In order to really try it out and see if web slices could be useful, I created a small ASP.NET project that has an order page and uses an RSS feed for the web slice, implemented via an ASP.NET Httphandler. The sample project can be downloaded here.

I've split up this into two blog posts because it would be such a lengthy post otherwise. This post is part 1 and explains the order page, part 2 will describe the Httphandler and the RSS feed.

This is an example of what this web slice looks like in IE 8.












This sample was created using C# on Vista / IIS 7, VS 2008 and SQL Server 2008. The database is using the AdventureWorks sample database. If you haven't installed it, you can download it from here.

I have not used any particular features that require IIS 7 or SQL Server 2008. I strongly recommend that you download and use one of the public Virtual PC 2007 images Microsoft have released that has IE 8 installed instead of installing the IE 8 beta on your main machine.

If you extract the zip file, you will see the 4 projects the solution contains:

  • SalesOrderWeb - main ASP.NET which include the sample order page
  • SalesOrderRSS - Httphandler and RSS feed logic
  • DAL - our data layer which retrieves the order information from the database
  • BusinessEntity - the domain layer, just contains the properties
The ASP.NET page only contains one page, Order.aspx. The page will either retrieve a random order from the database or retrieve a specific order if the user is requesting to view the order from the web slice. In our sample, the user will navigate to the same order page but in a real world scenario the user would probably navigate to a different view where they can track the order, contact customer service and so forth.

The Page_Load method contains the bulk of the work on this page.


protected void Page_Load(object sender, EventArgs e)
{

if (!Page.IsPostBack)
{
string salesOrderId = string.Empty;
DAL.SalesOrder dal = new SalesOrder();

// If this is an existing order
if (Request.QueryString.Count > 0 && Request.QueryString["SalesOrderId"] != null)
{
salesOrderId = Request.QueryString["SalesOrderId"].ToString();
}
else
{
salesOrderId = dal.RetrieveRandomSalesOrderId();
}

SalesHeader sh = dal.RetrieveSalesOrder(salesOrderId);
_salesHeader = sh;

GridView1.DataSource = sh.SalesDetails;
GridView1.DataBind();

labelSalesOrderNumber.Text = salesOrderId;

// Embed in the page the url of the RSS feed for this sales order.
SetRSSFeedURL(salesOrderId);
}
}

If we don't have a "SalesOrderId" value in the query string, we will assume this is a new order and we call into our data layer to retrieve a random order from the database. The bulk of the work to retrieve the random order in the DAL can be seen below.


using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT TOP 1 SalesOrderID FROM Sales.SalesOrderHeader ORDER BY NEWID()";

conn.Open();

using (SqlDataReader dr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection))
{
while (dr.Read())
{
salesOrderId = dr.GetInt32(0).ToString();
}
}
}
The use of the NEWID() SQL Server method is an interesting way to generate a random value like in our case but normally I would generate a random number in the code and not add extra work to the database.

After we have a SalesOrderId regardless if we retrieved a random sales order or via an existing order, we retrieve the details from the DAL via the RetrieveSalesOrder method. It contains quite a few inner joins, normally I would create a stored procedure or view but for ease of setting up this sample I decided to just set the sql in a StringBuilder object.


StringBuilder sqlStatement = new StringBuilder();
sqlStatement.Append("SELECT sh.SalesOrderNumber");
sqlStatement.Append(",sh.[Status]");
sqlStatement.Append(",sh.ShipDate");
sqlStatement.Append(",sh.DueDate");
sqlStatement.Append(",sh.TotalDue");
sqlStatement.Append(",sh.TaxAmt");
sqlStatement.Append(",sh.Freight");
sqlStatement.Append(",sd.LineTotal");
sqlStatement.Append(",sd.OrderQty");
sqlStatement.Append(",p.Name");
sqlStatement.Append(" FROM Sales.SalesOrderHeader sh");
sqlStatement.Append(" INNER JOIN Sales.SalesOrderDetail sd");
sqlStatement.Append(" On sh.SalesOrderId = sd.SalesOrderId");
sqlStatement.Append(" INNER JOIN Production.Product p");
sqlStatement.Append(" On sd.ProductID = p.ProductID");
sqlStatement.AppendFormat(" WHERE sh.SalesOrderId = {0}", salesOrderId);

After we have retrieved the sales order details, we set the results to the gridview. The DAL returns the domain business classes to the UI. Returning a SqlDataReader or similar one of the worst ways of coupling the database implementation to the presentation. I personally dislike returning a dataset too because it's such a bloated xml mess.

Finally, we call the SetRSSFEEDUrl method to set the RSS feed url for the web slice for this particular order. Because we are using a master page, we need to do a little extra work to find the "feedurl" control where we set the url.


private void SetRSSFeedURL(string salesOrderNumber)
{
// Find the feedurl href so we can set the correct url (RSS feed).
// Since this is on a master page, we have to through some hoops.
ContentPlaceHolder contentPlaceHolder;

contentPlaceHolder = (ContentPlaceHolder)Master.FindControl("ContentPlaceHolder1");
if (contentPlaceHolder != null)
{
Control ctl = (Control)contentPlaceHolder.FindControl("feedurl");

if (ctl != null)
{
HtmlAnchor anchor = (HtmlAnchor)ctl;

if (anchor != null)
{
anchor.HRef = string.Format("http://{0}/SalesOrderWeb/{1}/OrderTracking.rss"
, System.Net.Dns.GetHostName()
, salesOrderNumber
);
}
}
}
}
There are couple of things of interest that we have to set up in the web.config file. We are using a custom configuration section to store the default values for our RSS feed like title, description and so forth. First we define the type at the top of the web.config file like the below configuration shows.



At the bottom of the web.config, we display the default values like this.



We also set up the connection string to the database using the regular connection string element that was introduced with ASP.NET 2.0



Lastly, we set up the httphandler. We specify that we only want this handler to fire for GET requests. We also define that it should only run if the URL for this site ends with "OrderTracking.rss". Any other ".rss" handlers will be ignored unless they are listed in the web.config or machine.config. We also set the type where the httphandler can be loaded.



If we have set everything up correctly and publish the solution to our web site, our order page should look something like this when we "create" a new order.



That's it!

First post

Hello everyone, I will be writing about technological topics that interest me at the moment. I work with a variety of different technologies but will for most part write about Microsoft topics using .Net/C#/ASP.Net/SharePoint and RIA tech like Flex. First up I will show one example of how you can use ASP.NET and the new feature Web slices in IE 8.