Home
Archives
About us...
Advertising
Contacts
Site Map
 

ruby in steel

 

BLOGGING WITH DELPHI #5

Adding RSS Feeds. This is the last part of the series on creating a weblog using ASP.NET and ECO II. Next time, Bob will continue with Enterprise Core Objects III – trying to illustrate them using the existing example, as well as some new examples.
by
Bob Swart

Requirements:
Borland's Delphi 2005 Architect

 

This month's source code is provided in the text. The listing of the GenerateRSS method is HERE (in a popup window)

 

Continued from Part Four

Borland Delphi 2005 Architect contains a featureset called Enterprise Core Objects 2, which allows us developers to create applications based on a model (with objects, inheritance and associations), which can be made persistent in a DBMS, and used to create GUI as well as web applications. In this multi-part article, I’ll use Delphi 2005 and Enterprise Core Objects to define and implement an application handling web logs - also called blogs. This time, with the focus on RSS Feeds, adding an ASP.NET ECO Web Service to the existing ASP.NET ECO application.

ECO Web Service

Technically, we don’t need a web service to return the RSS feed – a special ASP.NET HttpModule would . In fact, the RSS feed itself will just be a static XML document, since it won’t change that often (so it’s actually more efficient to regenerate it when the EcoSpace is modified with a new Category or Post, but not more often). However, just for testing purposes, let’s assume we also want a web method to refresh the generated RSS feed file (so we don’t have to change the model just to test the RSS file generation).

If you want to play along, and reuse the existing model, just reopen the Weblog.bdsproj project, and then do File | New – Other, go to the New ASP.NET Files in the Delphi for .NET Projects category, and select the ECO ASP.NET Web Service icon, as shown below.

This will create a new ECO web page with files WebService1.asmx and WebService1.pas and add them to the Weblog project. Since the name WebService1.asmx will become part of the URL to call the Web Service, let’s rename WebService1 to RSS using File | Save As.

Now, inside the RSS.pas file, you’ll notice that the Web Service itself is called TWebService1, which also is not very descriptive of our particular web service, so let’s rename that TWebService1 to TRSSWebService (personally, I use refactoring for that).

Alternately, if you do not want to reuse the Weblog application, you can create a fresh new ECO ASP.NET Web Service project. In that case, you will need to design a model and make the model persistent (like we’ve done in the previous articles, so I won’t repeat that information here again).

RSS Web Service

Moving along to the design area for the RSS web service, you may notice that this design area RSS is not empty, as with a “regular” ASP.NET Web Service, but it already contains a component called rhRoot. This is actually the root handle, which should be assigned to our EcoSpace. We can make sure of that by pointing the EcoSpaceType property of the rhRoot component to WeblogEcoSpace.TWeblogEcoSpace.

Let’s now consider what kind of RSS feeds I want to offer. I want to offer an RSS file with the complete feed, but also individual RSS files for all categories. The latter could be made dynamic (based on the available category instances in the EcoSpace), but since it’s only my weblog, I’ve decided to hardcode the categories, as we’ll see in the first listing below.

As a web service interface, I want to expose a method that can refresh the static RSS files. Called RefreshRSS, it will call a method GenerateRSS that will produce an RSS feed for all posts in the given category.

The definition for the WebMethod RefreshRSS is as follows:

[WebMethod]
procedure RefreshRSS;

With the following implementation, calling a yet to be designed and implemented method GenerateRSS (the main topic of this article):

procedure TRSSWebService.RefreshRSS;
begin
  GenerateRSS(EcoSpace); // all posts
  GenerateRSS(EcoSpace, '.NET Framework', 'dotnet');
  GenerateRSS(EcoSpace, 'ADO.NET / BDP', 'adonet');
  GenerateRSS(EcoSpace, 'ASP.NET', 'aspnet');
  GenerateRSS(EcoSpace, 'Compact Framework', 'cfnet');
  GenerateRSS(EcoSpace, 'Conferences / Events', 'events');
  GenerateRSS(EcoSpace, 'Delphi', 'delphi');
  GenerateRSS(EcoSpace, 'Enterprise Core Objects', 'eco');
  GenerateRSS(EcoSpace, 'Windows Longhorn / Vista', 'vista');
  GenerateRSS(EcoSpace, 'XML, SOAP & Web Services', 'xml-soap');
  DoneWithEcoSpace;
end;

Note that my RSS feed will only contain the Posts, and not the Comments on the Posts. Also note that I want all Posts (from all Categories) to be put in the RSS feed when I call GenerateRSS without arguments, which leads to the following definition of the method:

class procedure GenerateRSS(EcoSpace: Borland.Eco.Handles.EcoSpace;
   const Cat: String = ''; const FileName: String = 'Weblog');

I’ve turned it into a class procedure, so anyone can call it, without the need for an instance of the TRSSWebService class. This means we can later also call it from the ASP.NET web form pages, for example after a Post has been modified (see the btnSaveChanges_Click event at the end of this paper).

ECO Web Methods

Before we can implement this RSS Web Method, however, we should first take a closer look inside the generated source code for our ECO web service. Here, you’ll find a number of example web methods to give you an idea of how to get your hands on the EcoSpace from an ASP.NET Web Service. Apart from the general HelloWorld example web method, there are two ECO specific examples: OrderCount and NewOrder. The first one is an example of a read-only web method, which leaves the EcoSpace intact. The second is a web method that modifies the contents of the EcoSpace. The main difference is that the latter needs to call UpdateDatabase before calling the DoneWithEcoSpace method. That’s actually no different than we did in the ASP.NET Web Forms application. However, unlike an ASP.NET Web Forms application, an ASP.NET Web Service doesn’t use visual controls to do the data binding. We don’t use DataGrids or TextBox controls now, so the use of an ExpressionHandle component is also unnecessary.

The example OrderCount web method shows how to read data and return it in a non-visual way. This automatically generated code is as follows:

// This is a minimal web method reading data,
function TWebService1.OrderCount: integer;
var
  OclService: IOclService;
  ResultElement: IElement;
begin
  OclService := EcoSpace.GetEcoService(typeof(IOclService))
as IOclService;
  ResultElement := OclService.EvaluateAndSubscribe(
nil,
     'Order.allInstances->size',
nil, nil);
  Result := Integer(ResultElement.AsObject);
  DoneWithEcoSpace;
end;

The EcoSpace used here is the EcoSpace property from the web service itself (note that the class for the OrderCount method is still TWebService1 and was not renamed to TRSSWebService by refactoring, because it is placed in comments). The get_EcoSpace method is implemented as follows:

function TRSSWebService.get_EcoSpace: Borland.Eco.Handles.EcoSpace;
begin
  if not
Assigned(fEcoSpace) then
  begin
    fecoSpace := TEcoSpaceProvider.GetSessionFreeEcoSpace;
    rhRoot.EcoSpace := fEcoSpace;
    // Set additional root handles here and in DoneWithEcoSpace
  end;
  Result := fEcoSpace;
end;

This code makes sure that the EcoSpace is obtained from the EcoSpaceProvider (which in turn can retrieve it from the pool of available EcoSpaces).

Anyway, back to the example OrderCount method, which uses the EcoSpace property to call the GetEcoService method, checking to see if the IOclService is implemented, returning the IOclService as result. Using the IOclService, we can evaluate OCL expressions, which is just what we would do using ExpressionHandles in regular (visual) ECO applications.

Note that the Delphi online help seems to suffer from a little copy-and-paste daemon when it describes the way to obtain the IOclService (by incorrectly using the typeof(IStateService), which is also used to describe the way to obtain most of the other ECO interfaces. Obviously, we need to pass the actual typeof(Ixxx) here.

IOclService

The IOclService interface offers a number of helpful methods to evaluate OCL expressions, including the EvaluateAndSubscribe method to evaluate the given OCL expression and subscribe to the result. The Delphi 2005 online help is a bit sparse here, and also forgets to list a number of additional methods, like the simple Evaluate method that returns an IElement. This method – which is mentioned in the Delphi 2006 helpfile - is actually more useful in the stateless ASP.NET environment, since I don’t need the subscribe trigger to obtain the result of my OCL expression, so in my opinion, the example call to OclService.EvaluateAndSubscribe could have been replaced by OclService.Evaluate instead.

Once we have the resulting IElement, we can cast it to an Object using the AsObject method. This will either return a simple value, like with the example expression, or an ECO class type that we can then cast to the correct ECO type and use (like I’ll be needing for all Posts of a selected Category).

Let’s see how the code for my GenerateRSS would look like. First of all, we also need to obtain the IOclService, as follows:

var
  OclService: I OclService;
begin
  OclService := EcoSpace.GetEcoService(typeof(IOclService)) as IOclService;

and then I want to find all instances of Posts, ordered (descending) by the date they are posted. This can be encoded as follows:

OclService.Evaluate('Post.allInstances->orderdescending(Posted)')

The special issue we have to take into account, is that this evaluation doesn’t return a single value or class instance, but rather a collection of Posts (even if there are 0 or just 1 Post, you will get a collection as result). So instead of a single IElement, we must assign the result to an IElementCollection, which changes the code to:

var
  ResultElements: IElementCollection;
...
  ResultElements :=
   OclService.Evaluate('Post.allInstances->orderdescending(Posted)').GetAsCollection;

And now we can iterate through the ResultElements and cast them as Post instances, as follows:

var
  Posts: Post;
...
  for i:=0 to ResultElements.Count-1 do
  begin
    Posts := (ResultElements[i].AsObject as Post);

The full source code for the GenerateRSS method will follow at the end of this article. First, let’s recall the fact that I wanted to pass an optional name for a specific Category, so we would only see Posts that belong to that category. That means a different OCL expression, selecting the Category with a specific name, and then returning the ordered Posts of that particular Category instance. Given that Cat is the string with the name of the Category, the OCL expression is as follows:

ResultElements :=
  OclService.Evaluate('Category->allInstances->select(c | c.Name = ''' +
     Cat + ''').Posts->orderdescending(Posted)').GetAsCollection;

The remaining code can be the same, working with the individual Post objects from the collection of Posts.

Producing RSS (XML)

The final step is simple now, and consists of nothing more than producing a static XML page with the RSS of the selected Posts. An RSS feed is in fact nothing more than an XML document using a specific set of allowed elements and values.

I’ve used the XmlTextWriter class to produce the RSS feed in the RSS subdirectory of my virtual directory on the web server. To get the RSS subdirectory, I should first find out where the Weblog virtual directory itself is located, which can be done by calling the Server.MapPath method, as follows:

HttpContext.Current.Server.MapPath('\Weblog') + '\RSS\Weblog.xml', nil);

After that, it’s just a matter of specifying the formatting (none instead of indenting, since I would expect no human to want to read the RSS XML), and the rss starting element.

The more challenging problem I faced here was the format of the dates. RSS checkers gave me errors when I tried to format the DateTime fields (like Posted) to a string, since RSS is very strict on the date format. I had to construct a special DateTime formatting expression as follows:

Feed.WriteElementString('pubDate',
   Posts.Posted.ToString('ddd, dd MMM yyyy HH:mm:ss zz')+'00');

Note the extra two '00' characters that I had to append, since the normal +1 would not be accepted by the RSS format checkers. This is one of those times where I hate dates, but at least it all works now.

You can view the complete listing for the GenerateRSS method HERE (in a popup window)

As you can see in the code of this method, I’m using the Summary field in the RSS feed, instead of the complete contents of the blog Post. If a Summary is not available, then I’m taking the first line from the blog Posts (i.e. the text in the Contents before a dot followed by a space, carriage return or line feed).

I also include a link to the blog Post, and for that I need the Id of the Post, which can be obtained by calling the IdForObject function from the IdService interface, passing the Posts.AsIObject as argument, as follows:

Feed.WriteElementString('link', WeblogUrl +
    IdService.IdForObject(Posts.AsIObject));

Note that the actual WeblogUrl is obtained from the AppSettings in the web.config, right next to the ECO Pool settings, as follows:

<appSettings>
   <add key="Borland.Eco.Web.MaxPool" value="0" />
   <add key="Borland.Eco.Web.MaxAge" value="600" />
   <add key="URL" value="http://85.146.33.29/Weblog/Blog.aspx?RootId=" />
</appSettings>

And finally, we can incorporate the call to GenerateRSS into the btnSaveChanges_Click, so a new RSS file will be generated each time a Post is modified (by me).

procedure TWebForm1.btnSaveChanges_Click(sender: System.Object; e: System.EventArgs);
begin
  with
(rhRoot.Element.AsObject as Post) do
 
begin
    Title := tbTitle.Text;
    Contents := tbContents.Text;
 
end;
  UpdateDatabase;
  TRSSWebService.GenerateRSS(EcoSpace);
// new: calling class method GenerateRSS
  DataBind
end;

Which produces the RSS that you can access from http://www.drbob42.com/Weblog.xml as well as http://www.eBob42.com/Weblog.xml.

To read the RSS feed, you will need a feed reading application. Here, for example, we are viewing the feed using the free Awasu feed reader...

For more information, refer to The Bitwise Guide To RSS and our Review of Feed Readers


Next Time...

By the time you read this part, Delphi 2006 is almost released, and so is the next version of Enterprise Core Objects (dully called ECO III). There are a number of enhancements and new capabilities added to ECO III, which can be used to enhance the existing weblog application. As an example, the new state machine capabilities of ECO III can be used for the blog comments: ensuring that a comment which has just been submitted will be in the “just submitted” state, and will require at least one additional reviewing step before it’s in the “approved” state which will lead to its publication on the web. And that’s but one of the new features in ECO III, so stay tuned for next time (and don’t forget to subscribe to my RSS feed or leave me any comment, and I’ll get back to you next time with more blogging!

All this and more next time, so stay tuned...

Bob Swart (aka Dr.Bob - www.drbob42.com) is an author, trainer, developer, consultant and webmaster for Bob Swart Training & Consultancy (eBob42) in The Netherlands, who has spoken at Delphi and Borland Developer Conferences since 1993. Bob has written chapters for seven books, as well as the Borland Delphi 8 for .NET Essentials and Delphi 8. ASP.NET Essentials courseware manuals licensed by Borland worldwide, and is selling his updated Delphi 2005 courseware manuals online at http://www.drbob42.com/training. Bob received the Spirit of Delphi award at BorCon in 1999, together with Marco Cantù.

 

November 2005

 


Home | Archives | Contacts

Copyright © 2006 Dark Neon Ltd. :: not to be reproduced without permission