|
roomie, or: how to create the perfect internet bulletin board (visit roomie - hebrew version. english will be available soon.) |
|
You probably have used a bulletin board on the internet many times. I always feel that there's something missing; a field or feature which would make my life a whole lot easier. During the past month I've started using the flat-renting boards more often, as I've been looking for a new apartment. The experience has left me quite shaken (not stirred, though), and so I began to wonder whether it was indeed that difficult a task? Armed with the ASP.NET, I embarked on a journey to the unknown realm of bulletin boards making... The main thing to remember before you start is that you must think like an end user. This might seem like a trivial task, but I assure you, it isn't. Don't hesitate to flood your friends with beta versions and ask their opinion. I had a good start because of my current flat-predicament (this is why I decided to create a flat-to-rent board). The most annoying thing about most boards is the lack of a good search engine. So I made sure mine would be a damn good one, not missing out any possible features.
Roomie was created using the .NET Framework and Visual C# .NET (which is a part of the Visual Studio .NET The Database I'm using Microsoft SQL Server 2000 as my database server. Here's the database's diagram:
As you can see, most of the data is stored in the tables RentAds and RentSearches. There are also two other tables MateAds and MateSearches which are linked in a one-to-one relationship to the first two (respectively). This is used instead of creating two identical tables of each kind (with the addendum of the roommate fields). The other tables are Users which is used for signing-in and matching ad and search records to their owners, Shires (yeah, I'm a Tolkien addict ;) which are the geographical areas used as the main sorting field, and CurrencyRates which is used to convert the apartment prices from one currency to the other (in Israel, rent fees are usually in US Dollars, for some vague reason). I utilize SQL stored procedures to retrieve and update records. The most complex of which - RentSearch - is used to retrieve the ads according to the specified parameters. Displaying the Data Data access is one of the most powerful features of ASP.NET. The System.Web.UI.WebControls.DataGrid class databinds directly to the SQL query results and displays them in any way I choose. Let's see some code (note that this is not exactly the code in the Roomie available online; I've shortened it a bit): |
<asp:DataGrid id="DataGrid1" runat="server" AutoGenerateColumns="False"
EnableViewState="False">
<Columns>
<asp:TemplateColumn HeaderText="Rent ($)">
<ItemTemplate>
<%# ((bool)DataBinder.Eval(Container, "DataItem.LocalCurrency")) ?
((int)DataBinder.Eval(Container, "DataItem.RentFee") /
currencyRate).ToString(".00") :
("<span style=\"color: red;\">" + DataBinder.Eval(Container,
"DataItem.RentFee") +
"</span>") %>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="Rooms" HeaderText="Rooms"></asp:BoundColumn>t;
<asp:TemplateColumn HeaderText="Details">
<ItemTemplate>
<span class="link" onclick='<%# "showAd(" +
DataBinder.Eval(Container, "DataItem.AdID") +
")" %>' onmouseout="handleLink()" onmouseover="handleLink()">
äöâ</span>
<xml id='<%# "oData" + DataBinder.Eval(Container, "DataItem.AdID") %>'>
<Data>
<Payments><%# DataBinder.Eval(Container, "DataItem.Payments") %>
</Payments>
<Furniture><%# DataBinder.Eval(Container, "DataItem.Furniture") %>
</Furniture>
<Notes><%# Server.HtmlEncode((string)DataBinder.Eval(Container,
"DataItem.Notes")) %></Notes>
</Data>
</xml>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:datagrid>
|
What I've done in the last TemplateColumn is take some of the fields and put them in an xml element. Later, I used an ECMAScript function to add a row bellow the clicked row with the extra details formatted from the XML. This method has a few advantages: you don't have to go back and forth between pages, only one round-trip to the database is required, and the XML does not take as much bandwidth as formatted HTML. Another important issue, bandwidth-wise, is setting the DataGrid's EnableViewState property to False. The ViewState is a very useful thing, but it consumes a lot of bandwidth, especially for DataGrids. I only disabled the ViewState for the DataGrid as it is important for the other controls (search fields) in this page. For more information about ViewState, please refer to the .NET Framework SDK documentation. And, last, for the search page, a feature many ignore - a simple sorting mechanism. The database retrieves the ads sorted by RentFee (it is actually a more complex sort, because RentFee may contain prices from two different currencies.) For additional sorting, I've used the DataGrid.Sort property. The User Center I was very content with my search engine, but I still felt I need an extra edge to make this complete. Thus, the User Center was born. After a simple registration (email and password only), you may enter the center and use it to save searches or add new advertisements to the board. I found the save search feature most helpful due to the large amount of search criteria. It was also quite easy to implant using the ASP.NET Web Controls object model. Here's a code sample for retrieving a saved search (again, this is only a subset of the actual code): |
if(Request.QueryString["searchId"] != null) { try { Components.RentDB rdb = new Components.RentDB(); Components.RentSearch rs = rdb.GetRentSearch(Int32.Parse( Request.QueryString["searchId"])); cShireID.SelectedItem.Selected = false; cShireID.Items.FindByValue(rs.shireId.ToString()).Selected = true; cRoomsFrom.SelectedItem.Selected = false; cRoomsFrom.Items.FindByValue(rs.roomsFrom.ToString()).Selected = true; cAvlFromDay.SelectedIndex = rs.availableFrom.Day - 1; cAvlToDay.SelectedIndex = rs.availableTo.Day - 1; try { cAvlFromYear.SelectedItem.Selected = false; cAvlFromYear.Items.FindByValue(rs.availableFrom.Year.ToString()).Selected = true; } catch(Exception) {} cLocalCurrency.Checked = rs.localCurrency; cFloorFrom.Text = rs.floorFrom.ToString(); cFloorTo.Text = rs.floorTo.ToString(); cRentFee.Text = (rs.rentFee == 0) ? "" : rs.rentFee.ToString(); cStreet.Text = rs.street; cNotes.Text = rs.notes; } catch(Exception) {} }
|
The Components.RentSeach is a struct I wrote to contain all the data of a saved search. As you can see, I'm using error-trapping to avoid error messages which may occur due to incompatible data. Conclusion
It seems that the journey wasn't that rough after all (which makes you wonder why people hadn't taken it yet; maybe it's because they didn't have ASP.NET!) Future releases might include an automatic mailer, which will send new messages to the user's email from a saved search, and information retrieval using XML Web Services. |
