Using Strongly Typed Objects and Collections to replace DataSet’s in your .NET applications.
 
Published: 01 May 2004
Unedited - Community Contributed
Abstract
This piece discusses the advantages of using Strongly Typed objects in your applications - this method of coding can save you hundreds of hours if implemented properly.
by Phil Winstanley
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 24040/ 31

Reasons for the Switch

Indexers, are one of the ugliest methods of accessing Data stored in objects, they do not lend them selves to the eye, and using DataSet’s you will find yourself using the indexers a lot, below is a line of code showing the use of Indexers in a DataSet.

string myFooRegistration = myDataSet.Tables[0].Rows[1][2].ToString();

The above does not seem particularly elegant in comparison to a Strongly typed object such as the one shown below which only exhibits one indexer to achieve the same: -

string myFooRegistration = Foos[1].Registration;      

In the same vein, it is possible to make your Collection Indexer’s even more Developer friendly by using Strings or any other Data Type as your indexing identifier, for example below a string (Foo Registration) is used as the indexer: -

string myFooRegistration = Foos[“DMN 666”].RegistrationDate;      

Weakly Typed fields in DataSet’s lead to confusion and can very easily end up with people using the wrong Type for the Data that is being represented. It’s much more difficult to get a number out of a DataSet than it is to get a number out of a Strongly Typed object.

int myFooId = ((int)myDataSet.Tables[0].Rows[1][0].ToString());

In comparison to: -

int myFooId = Foos[1].Id;

Knowledge of the Data Structure, developers need to know the structure/schema of the Data stored within DataSet’s to get the best out of them, and DataSet’s do not lend them selves to this aim, however when using Strongly Typed Objects your Data Structure is already mapped out in Properties and Methods on your objects.

As a Developer using a DataSet you need to know what the field names are called exactly or at which index in your DataRow’s the field you are looking for can be found: -

int myFooId = ((int)myDataSet.Tables[0].Rows[1][0].ToString());

int myFooId = ((int)myDataSet.Tables[0].Rows[1][“Id”].ToString());

The above is acceptable if there will only be one developer working with the code (a personal project for example), however in a team environment Developers find code that takes the responsibility away from them understanding and knowing the Data Schema much easier to work with.

Special Methods, cannot be added to your DataSet’s easily, however using Strongly Typed objects it is possible to have methods sitting inside your objects that can expose business logic, for example using the Strongly Typed Foo object we can perform actions that would with DataSet’s be a separate method call: -

Foos[1].SubmitToQA();

This kind of functionality is even more useful when thinking about Collections of objects, for example you may want to do some filtering on the Foos Collection, you may need to know what the Minimum and Maximum Price’s of the Objects in the Collection are, you’d just add two properties to the FooCollection class as so -

double MinimumPrice = Foos.MinimumPrice;

double MaximumPrice = Foos.MaximumPrice;

Strongly Typed Objects within Strongly Typed Objects, are very easy to create and use, and again they follow the Data Structure issues that have been mentioned above. Taking our Foo object again, we’ll look at a sample of this functionality.

int myDerivativeId = myFoo.Derivative.Id;

But even nicer than the above is the ability to have Collections within your objects, so you might have something like this: -

string myOptionName = myFoo.Options[0].Name;

Or Even: -

foreach(Option O in myFoo.Options)

{

          Response.Write(O.Name + “<br />”);

}

Data Binding, is just as easy with Strongly Typed Objects as it is with DataSet’s. And in some cases it’s much easier, for example the Fields you need are all available in strongly typed names, and when you’re working with VS.NET with inline Code there is no Intellisense to help you out, friendly names are much more helpful. The Code Behind methods of Data Binding work as normal: -

Foos.DataSource = myFoos; //myFoos is of type FooCollection

Foos.DataBind();

Your inline Data Binding code is also the same: -

<asp:Repeater ID="Foos" Runat="server">

<ItemTemplate>

<SPAN class="body">Title: <%# DataBinder.Eval(Container.DataItem,"Title")  %></SPAN><br />

<SPAN class="body">Mileage: <%# DataBinder.Eval(Container.DataItem,"Miles","{0:N0}")  %></SPAN<br />

<SPAN class="body">Price: <%# DataBinder.Eval(Container.DataItem,"Price","{0:C0}")  %></SPAN><br />

</ItemTemplate>

</asp:Repeater>

You can even access your Strongly Typed objects where normally you would have to use ItemArray’s: -

private void Foos_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)

{

          BusinessObjects.Foo myFoo = new BusinessObjects.Foo();

          if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem )

          {

                   myFoo = ( ( BusinessObjects.Foo ) e.Item.DataItem );

          }

          System.Web.UI.HtmlControls.HtmlGenericControl PriceChangeSection = ((System.Web.UI.HtmlControls.HtmlGenericControl )e.Item.FindControl("PriceDifferenceSpan"));

          if (PriceChangeSection != null)

          {

                   if (myFoo.OldPrice > 0)

                   {

                             PriceChangeSection.Visible = true;

                   }

                   else

                   {

                             PriceChangeSection.Visible = false;

                   }

          }

}

Unit Testing, is very simple using Strongly Typed Objects, you can create them in code, you don’t need a Database to be in place to retrieve Data from, and you can test your Business Logic in the same place as you’re testing your Data Structure.

The Strongly Typed Object

The easiest way to look at building objects is by first thinking about the logical structure of your data. Using the idea of a Foo we shall construct a logical Data Hierarchy of objects.

Start at the very top of the tree, then move down, firstly the Foo class.

using System;

namespace BusinessObjects

{

          public class Foo

          {

                   private int _Id = -1;

                   public string Id

                   {

                             get

                             {

                                      return _Id;

                             }

                             set

                             {

                                      _Id = value;

                             }

                   }

                   public Foo()

                   {

                   }

          }

}

Initially there is just one property on the object, this is enough for us to be able to Identify which Foo the Class is. Also it is handy to be able to pre-populate the properties on the class, this is achieved by using constructors.

namespace BusinessObjects

{

          public class Foo

          {

                   private int _Id = -1;

                   public string Id

                   {

                             get

                             {

                                      return _Id;

                             }

                             set

                             {

                                      _Id = value;

                  

                   public Foo()

                   {

                   }

                   public Foo(int FooId)

                   {

                             _Id = FooId;

                   }

          }

}

We now have some code that is simple to understand and very easy to use, some sample usage is here: -

private void DoSomething()

{

          BusinessObjects.Foo C = new BusinessObjects.Foo(500002112);

          Console.WriteLine(C.Id);

}

The Strongly Typed Collection

Having one of an object is useful, but being able to group objects of the same type together in to a collection is essential. What follows is the code to create an object in which to store the Foo objects.

using System;

using System.Collections;

namespace BusinessObjects

{

          public class FooCollection : CollectionBase

          {

                   public Foo this[int index]

                   {

                             get { return (Foo) this.List[index]; }

                             set { this.List[index] = value; }

                   }

                   public void Add(Foo myFoo)

                   {

                             if (!DoesFooExist(myFoo.Id))

                             {

                                      this.InnerList.Add(myFoo);

                             }

                   }

                   public bool DoesFooExist(int FooId)

                   {

                             bool flag = false;

                             foreach(Foo C in this)

                             {

                                      if (C.Id == FooId)

                                      {

                                                flag = true;

                                                break;

                                      }

                             }

                             return flag;

                   }

                   public FooCollection ()

                   {

                   }

          }

}

What we have above is a Class that inherits from CollectionBase, we’ve added an Indexer, an Add Method and a Method to check if a Foo object is already contained within the collection.

What follows is sample usage of the Collection object.

private void DoSomethingElse()

{

          BusinessObjects.Foo Foo1 = new BusinessObjects.Foo(1);

          BusinessObjects.Foo Foo2 = new BusinessObjects.Foo(2);

          BusinessObjects.FooCollection CC = new BusinessObjects.FooCollection();

          Console.WriteLine("Foo in the Collection? : " + CC.DoesFooExist(Foo1.Id));

          CC.Add(Foo1);

          CC.Add(Foo2);

         

          Console.WriteLine("Foo in the Collection now ? : " + CC.DoesFooExist(Foo1.Id));

          Console.WriteLine("Foo in the Collection using an Index? : " + Convert.ToString(Foo2.Id == CC[0].Id));

          Console.WriteLine("Foo in the Collection using an Index? : " + Convert.ToString(Foo2.Id == CC[1].Id));

}

The result from the above would be: -

Foo in the Collection? : False

Foo in the Collection now ? : True

Foo in the Collection using an Index? : False

Foo in the Collection using an Index? : True

Populating objects and collection from Databases

The function of populating your objects is relatively simple, all you basically need to do is retrieve the Data from it’s store and pass it in to your objects.

The best way of achieving this is by having all of your Data Access code in one place, then using that code throughout your applications. The Data access code can be project specific or more general and this will depend on your needs.

An example of this follows: -

 

public FooCollection PoshFoos()

{

          //Setup some Variables

          string ConnectionString = ("MySqlServer");

          string StoredProcedure = ("MyStoredProc");

          FooCollection CC = new FooCollection();

          //Set up the Parameters for the Database Code

          NameValueCollection Params = new NameValueCollection();

          Params.Add("@FoosExistInOtherTable","FooDatabase.dbo.PoshFoos");

          //Retreive the Data using our custom call (DAL)

          DataSet Foos = DataAccess.Out.GetDataSet(ConnectionString,StoredProcedure,Params);

          //Loop through the rows collected and populate the Foo Object

          foreach(DataRow DR in Foos.Tables[0].Rows)

          {

                   Foo C = new Foo();

                  

                   C.Id = Convert.ToInt32(DR["FooId"].ToString());

                   C.Title = DR["Title"].ToString();

                   C.ExteriorColor = DR["ExteriorColour"].ToString();

                   C.InteriorColor = DR["InteriorColour"].ToString();

                   C.InteriorMaterial = DR["InteriorMaterial"].ToString();

                   C.Price = Convert.ToDouble(DR["Price"].ToString());

                   C.Miles = Convert.ToInt32(DR["Miles"].ToString());

                   C.Registration =  DR["Registration"].ToString();

                   C.Transmission = new Transmission(DR["TransmissionCode"].ToString(),DR["TransmissionName"].ToString());

                   C.Fuel = new Fuel(DR["FuelCode"].ToString(),DR["FuelName"].ToString());

                   C.Manufacturer = new Manufacturer(Convert.ToInt32(DR["ManufacturerId"].ToString()),DR["ManufacturerName"].ToString());

                   C.Model = new Model(Convert.ToInt32(DR["ModelId"].ToString()),DR["ModelName"].ToString());

                   C.Derivative = new Derivative(Convert.ToInt32(DR["DerivativeId"].ToString()),DR["DerivativeName"].ToString());

                   C.Description = DR["Description"].ToString();

                   C.HoldingCenter = new Center(Convert.ToInt32(DR["CenterId"].ToString()),DR["CenterName"].ToString());

                   CC.Add(C);

          }

          //Return the Foo object

          return CC;

}

 

Sometimes you will even want to encapsulate the above and add application logic such as Caching, and example of which follows: -

 

public BusinessObjects.FooCollection All()

{

          FooCollection CC = new FooCollection();

         

          if (System.Web.HttpContext.Current.Cache["PoshFoos_" + _Results.ToString()] != null)

          {

                   CC = ((FooCollection)System.Web.HttpContext.Current.Cache["PoshFoos_" + _Results.ToString()]);

          }

          else

          {

                   ReturnFoos RC = new ReturnFoos();

                   RC.NumberOfRowsToReturn = _Results;

                   CC = RC.PoshFoos();

System.Web.HttpContext.Current.Cache.Add("PoshFoos_" + _Results.ToString(),

CC,

null,

DateTime.Now.AddMinutes(Convert.ToInt32(60),

System.TimeSpan.Zero,

System.Web.Caching.CacheItemPriority.Normal,null);

          }

         

          return CC;

}

 

Another way, sometimes the best for persuading others to adopt this technique is by having methods on your objects that perform the data access, an example of which follows.

 

Foo.Select(123456);

Foo.Update();

Foo.Delete();

Whilst this moves away from a disconnected architecture it allows developers to get away without having to perform any manual Data Access.

It’s best to place all Data Access code that is used inside your objects in a separate Data Access class of some sort, this way you do not have to clutter your objects with a lot of methods to retrieve data, but instead instantiate your Data Access class, give it the Object you want Filling, and pass it the Criteria against which you wish the SQL Queries to be run. This is Similar to the way DataAdapter’s are currently used. 

There is no “right” was to populate your objects, all have their pitfalls.

Sorting and Filtering Strongly Typed Collections

Filtering

Whilst you can still use SQL Queries against your Database to perform searches, you can also perform searches against your collections. This is achieved by running you Collection through a filter. It’s easy to set up a Filter Class as s -

public class FilterFoos

{

 

          private double _PriceFrom = -1;

          public double PriceFrom

          {

                   set

                   {

                             _PriceFrom = value;

                   }

                   get

                   {

                             return _PriceFrom;

                   }

          }

 

          private FooCollection _Foos;

          public FooCollection Foos

          {

                   set

                   {

                             _Foos = value;

                   }

          }

 

          public FooCollection Filter()

          {

 

                   FooCollection Results = _Foos;

 

                   //If we have a Minimum Price let's filter the Foos by that

                   if (_PriceFrom > 0)

                   {

                  

                             FooCollection G = new FooCollection();

 

                             foreach(Foo C in Results)

                             {

                                      if(C.Price >= _PriceFrom)

                                      {

                                                G.Add(C);

                                       }

                             }

 

                             Results = G;

 

                   }

 

          return Results;

 

}

 

}

 

The above only has one ”Filter” inside it, but it’s really simple to set up more filters, and the above can be very easily used with your Strongly Typed Objects: -

 

private FooCollection DoFilter(FooCollection CC)

{

 

FilterFoos F = new FilterFoos();

 

if (Request.Form["PriceFrom"] != null)

{

                   F.PriceFrom = Convert.ToDouble(Request.Form["PriceFrom"].ToString());

}

 

//Perform the Filter on Price From

F.Foos = CC;

CC = F.Filter();

 

return CC;

}

 

Sorting

The simplest way of having your Collections sorted is by running the “Order By” clause in your SQL Lookup, and then passing the Rows in to your collection, however you may have in memory collections, and to perform sorting on these you will need to use objects called comparers and the ICompare interface, with a sprinkle of Array.Sort().

A Comparer Class needs to be created for the Properties of your objects that you wish to sort on as follows: -

 

public class FooTitleComparer : IComparer

{

          public int Compare(object x, object y)

          {

                   Foo xFoo = ((Foo)x);

                   Foo yFoo = ((Foo)y);

                   return String.Compare(xFoo.Title,yFoo.Title);

          }

          public FooTitleComparer()

          {

          }

}

 

Then in the Collection a Sort method needs to be created: -

public void Sort(string SortProperty)

{

          if (SortProperty.ToLower() == "title")

          {

                   FooTitleComparer myFooTitleComparer = new FooTitleComparer();

                   this.InnerList.Sort(myFooTitleComparer);

          }

}

 

Usage of the Sort method in your code is very simple to -

 

C.Sort("Title");

Advanced Sorting

public void Sort(string SortProperty)

{

          if (SortProperty.ToLower() == "title")

          {

                   FooTitleComparer myFooTitleComparer = new FooTitleComparer();

                   this.InnerList.Sort(myFooTitleComparer);

          }

}

Whilst the above is very flexible it does not allow us to sort in multiple directions (Ascending and Descending). To achieve this functionality more work has to be done with the Comparer Classes. An article covering this can be found here: -

http://www.c-sharpcorner.com/Code/2002/June/CollectionObgOrdering.asp



User Comments

Title: Mr.   
Name: Alan
Date: 2007-12-28 6:00:51 AM
Comment:
Another reason to use strongly typed objects over datasets is the size of a dataset... For instance if you are building a distributed app and want to send object over the wire to an app server then the overhead of using datasets is huge just because of their serialization size when comapred with strongly typed objects.

Travel boy this is why strongly typing datasets is not a great idea most of the type. You want to avoid the dependency on datasets too.
Title: what about dynamic connection ?   
Name: Sam
Date: 2006-07-19 5:55:06 PM
Comment:
Hi,
Nice article. But you don't go through connection strings. One problem I have is how to pass DYNAMICALLY a connection string to the dataAdapters at runtime. Say that I have a different connection string for each user, stored in a Session variable, how would I pass that to the xsd each time I want to use of a dataTable ?
Title: UML   
Name: Magnus Andersson
Date: 2005-06-10 4:18:14 AM
Comment:
WowWowWow !

Now i can design strongly typed object(dataset) in the whole project, without XML Schema.
This is great when i working with UML Design, because
ADO.NET object is a mess in UML design.
It is now time to build a automatic UML to dataset Class generator
Title: travelboy   
Name: travelboy
Date: 2005-06-08 12:15:54 PM
Comment:
Would advise looking into typed datasets as oppossed to spending hours defining your own collections.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-29 1:41:07 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search