views:

270

answers:

3

I'm struggling with bridging the concepts of good database design with good object orientated design.

Traditionally if I wanted to display a list of news stories in a repeater, I would use something like:

<script runat="server">

    void ShowNews()
    {
     rptNewsStories.DataSource = News.GetAllNews(); // Returns a DataTable
     rptNewsStories.DataBind();
    }

</script>

<asp:Repeater id="rptNewsStories" runat="server">
    <ItemTemplate>
     <div>
      <span class="Title"><%# Eval("Title")"%> (<%# Eval("Location")"%>)</span>
      <p>
       <%# Eval("Summary")"%>
      </p>
      <ul>
       <li>Added by: <%# Eval("AddedByFullName")%></li>
       <li>Added on: <%# Eval("AddedOn")%></li>
      </ul>
     </div>
    </ItemTemplate>
</asp:Repeater>

Here News.GetAllNews() returns a DataTable, which is just a dump of what the stored procedure returns. The stored procedure is written to return data by using joins, so it's more than one tables worth of data.

The advantage of this in that in the database the stored procedure can look up who added the news story from the AddedByID that exists in the News table and return the persons full name as the AddedByFullName value returned.

However if I try and drop the use of a DataTable and instead return a List of the News objects, I get the following:

<script runat="server">

    void ShowNews()
    {
     rptNewsStories.DataSource = News.GetAllNews(); // Returns a List<News>
     rptNewsStories.DataBind();
    }

</script>

<asp:Repeater id="rptNewsStories" runat="server">
    <ItemTemplate>
     <div>
      <span class="Title"><%# Eval("Title")"%> (<%# Eval("Location")"%>)</span>
      <p>
       <%# Eval("Summary")"%>
      </p>
      <ul>
       <li>Added by: <!-- Here there is only a AddedByUserID, not an AddedByFullName value --></li>
       <li>Added on: <%# Eval("AddedOn")%></li>
      </ul>
     </div>
    </ItemTemplate>
</asp:Repeater>

But now I'm left with the problem that certain values that I want to display (Like AddedByFullName) don't exist within the News object, because they're not something that's explicitly set, but instead of retrieved from a lookup ID in the object.

I'd like to return objects rather than DataTables, but I don't know the best way to bridge this gap.

Do I:
* Create additional properties in the News class for every additional value that can be returned from the database in relation to this data?
* Stick with DataTables for specific cases where that are lots of additional values?

Or am I just totally on the wrong track!

A: 

Good question:

There are a couple of approaches you can take:

  1. You can retrieve the additional data that you need on the binding event of the repeater and populate it into a "placeholder" for each news record returned. This has performance issues because of the additional querying but it will work.

  2. If possible I'd modify the news object to contain the additional information, or create an object that inherits from the news object that had the additional information or properties contained within it.

Hope this helps :-) +1

Edit/Comment: I think the friction you are seeing in your design is that you aren't acknowledging the "has-a" relationship between your news object and what I'd call its author/contributor. The join you are doing in your query is just disguising the lack of the relationship in the object model.

Achilles
Thanks for the input Achilles:1) Similar to Gregoire's suggestion. Like you say it will do the job, but this type of approach just seems like extra work? 2) I know this will be more efficient, but it still feels a little spikey... although I prefer it over option 1I purpose I'm hoping for a suggestion that feels right, where as these approaches don't quite feel like a perfect fit.
Peter Bridger
I think the friction you are seeing in your design is that you aren't acknowledging the "has-a" relationship between your news object and what I'd call its author/contributor. The join you are doing in your query is just disguising the lack of the relationship in the object model.
Achilles
Good point, well made
Peter Bridger
A: 

You can try this, perhaps not the better solution:

void ShowNews()
{   
    User[] usersConcerned = News.GetAllUsersLinkedWithNews(); //only return the users concerned by the news
    List<News> news = News.GetAllNews();
    foreach(News item in news)
    {
       item.AddedByUser = usersConcerned.FirstOrDefault(u=>u.Id == item.AddedByUserID);
    }
    rptNewsStories.DataSource = news ; 
    rptNewsStories.DataBind();
}
Gregoire
This type of approach did cross my mind, but it seems to be doing something that would be better handled by a join in a stored procedure. But then perhaps this is more OO? :)
Peter Bridger
@Peter, you should do the data filtering at the database level. You could pass in New.GetAllNews(usersConcerned) and handle the array in your proc. This could dramatically reduce the amount of network traffic depending on your data.
Nathan Koop
+1  A: 

Your choices are restricted by the underlying technology you're willing to use as data access. At the moment there are several alternatives that the VS toolset and .Net framework supports:

From these, all but ADO.Net typed data sets allow you to specify navigation relations like you ask, either eagerly or lazily loaded. In a case like you describe the natural hing to do with these technologies would be to model the relationship between News article and Author explicitly in the designers and let the framework deal with the problem of loading the appropriate data and into the appropriate types, ultimately meaning that the join issue is handled implicitly by the application data access layer (the framework) rather than by an explicitly created stored procedure.

The choice of technology will ripple everywhere in your code, from how you fetch your data to how you display it and how you update, none of these technologies are easily interchangeable. Personally, I find that the right balance of power and complexity is with LINQ to SQL.

Remus Rusanu
Thanks for your input Remus - I heard MS was stopping development of LINQ to SQL in favour of the Entity Framework. Is this true and if so, would you encourage new developments using this technology?
Peter Bridger
@Peter: I'm no longer with MS so I wouldn't really know more than is public knowledge. Is true that EF is the recommended way in the future, but given the overwhelming adoption of LINQ to SQL in comparison with EF, I'd be surpsied to see it completely abandoned.
Remus Rusanu
I'm currently using and enjoying LINQ2SQL for a .net 3.5 project. My next big project will use .net 4.0, so I'll be using the Entity Framework 4.0
Peter Bridger