Data Access

Last post 09-26-2008 11:58 AM by marcschluper. 8 replies.

Sort Posts:

  • Data Access

    12-10-2004, 12:45 AM
    • Member
      25 point Member
    • marcschluper
    • Member since 09-18-2003, 4:39 PM
    • Posts 15
    Can anyone explain to me why IssueTracker uses such an awkward way of accessing data? Why not simply use ADO.NET? There are tons of books about it and if everyone uses the same methods for accessing data we can all learn from each other and quickly start at a new job.
    IssueTracker uses code like this:

    protected CollectionBase GenerateIssueCommentCollectionFromReader(IDataReader returnData)
    {
    IssueCommentCollection cmtCollection = new IssueCommentCollection();
    while (returnData.Read())
    {
    IssueComment newComment=new IssueComment((int)returnData["CommentId"], (int)returnData["IssueId"], (string)returnData["Comment"], (string)returnData["CreatorUsername"], (string)returnData["CreatorDisplayName"], (DateTime)returnData["DateCreated"] );
    cmtCollection.Add(newComment);
    }
    return (cmtCollection);
    }

    Do you see any benefits over regular ADO.NET? Besides, the IssueTracker data access way is not even the fastest: for each row of the result table it again determines which column has name "CommentId", "IssueId", etc. Using GetOrdinal (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdatasqlclientsqldatareaderclassgetordinaltopic.asp) you get better performance.
    Marc Schluper
  • Re: Data Access

    12-10-2004, 2:34 AM
    • Member
      15 point Member
    • kevinlkk
    • Member since 12-05-2004, 10:55 PM
    • Hong Kong
    • Posts 3
    Moreover, is there any benefit to use collection class for each business function instead of using DTO? If the application is so large that there are many business function classes, it will be a nightmare of maintaining this "class boom".
    Kevin Leung
  • Re: Data Access

    12-14-2004, 6:52 PM
    • Participant
      873 point Participant
    • swalther
    • Member since 08-04-2002, 10:55 AM
    • Seattle, WA
    • Posts 173
    • AspNetTeam
    Hi Marc,

    Thanks for using the Issue Tracker!

    The Issue Tracker uses strongly typed collections to represent database data. For example, comments on issues are represented by the IssueCommentCollection class. A normal collection, such as an ArrayList, represents everything as an object. The advantage of strongly typed collections is that they represent their elements as the right data type (in this case, as IssueComment classes).

    The next version of the .NET Framework -- .NET Framework 2.0 -- has strongly typed collections built into it through a new feature named Generic Types. Using Generic Types, you can simply create a strongly typed collection of IssueComment objects like this:

    List<IssueComment> colIssueComments = new List<IssueComment>();

    I love generics! Unfortunately, with the current version of the .NET Framework (and ADO.NET), you must resort to building strongly typed collections yourself. And yes, that does add some ugly class bloat to the Business Logic Layer in the Issue Tracker.

    You also asked why the Issue Tracker does not use the GetOrdinal methods of the DataReader class when retrieving column values. You are correct that refering to a column in a DataReader by its ordinal position and not by name is faster.

    The downside of using ordinal references is that they make your code both more fragile and harder to read. Refering to a column by its ordinal position makes your code more fragile since, if you ever change the columns in your database table, your code will no longer work. Using numbers to refer to columns also makes your code much harder for humans to read and maintain. There are always tradeoffs... :)

    -- best,
    Stephen Walther
  • Re: Data Access

    12-20-2004, 6:01 PM
    • Member
      25 point Member
    • marcschluper
    • Member since 09-18-2003, 4:39 PM
    • Posts 15
    Thank you Stephen, for taking the time to anwer.

    The example shown on page MSDN does not make any assumption about the column number; it retrieves that number (once) by means of GetOrdinal and then uses the number to retrieve the cell value, for each row. So it does not use a fixed ordinal position.

    I use strongly typed collections myself. They do not enforce me to construct the kind of code Issue Tracker uses:
    SqlDataReader myReader = null;
    
    try
    {
    myConnection.Open();
    myReader = myCommand.ExecuteReader();
    int ciInstanceID = myReader.GetOrdinal("instanceID");
    int ciInstanceName = myReader.GetOrdinal("instanceName");

    while (myReader.Read())
    {
    MyClass myInstance = new MyClass();
    if (!myReader.IsDBNull(ciInstanceID))
    {
    myInstance.InstanceID = myReader.GetInt32(ciInstanceID);
    }
    if (!myReader.IsDBNull(ciInstanceName))
    {
    myInstance.InstanceName = myReader.GetString(ciInstanceName);
    }
    List.Add(myInstance);
    }
    finally
    {
    // etc
    }
    }

    Easy to read, simple, fast, standard ADO.NET.

    So the question remains: why do it differently, as in Issue Tracker?
    Why invent something new if it is not better?
    Marc Schluper
  • Re: Data Access

    03-20-2005, 2:04 AM
    • Member
      50 point Member
    • mmarksbury
    • Member since 11-11-2004, 12:37 AM
    • Posts 12
    Am I correct in my understanding that if this were written using Generics in ASP.NET 2.0, all of the collection classes would be eliminated?

    AND

    If so, how would the following Issue Tracker code be changed using Generics instead of string typed Collections?

    public static IssueCollection GetIssuesByProjectId (int projectId) {
    if (projectId <= DefaultValues.GetProjectIdMinValue())
    throw (new ArgumentOutOfRangeException("projectId"));

    DataAccessLayerBaseClass DBLayer = DataAccessLayerBaseClassHelper.GetDataAccessLayer();
    return (DBLayer.GetIssuesByProjectId(projectId));
    }
  • Re: Data Access

    03-20-2005, 3:28 AM
    • Member
      50 point Member
    • mmarksbury
    • Member since 11-11-2004, 12:37 AM
    • Posts 12
    Okay, so I did some digging, and believe I found a good way to replace the collection classes for the current Issue Tracker with minimal code, for ASP.NET 2.0 using generics. Please tell me if my solution will work, and if it is a good or bad implementation.

    First, I would Create a new class file called "GenericCollections.cs" or variation.

    This is the code it would contain . . .

    ---------------------

    using System;
    using System.Collections.Generic;

    namespace SPNET.StarterKit.IssueTracker.BusinessLogicLayer
    {
    public class CategoryCollection : List<Category>
    {
    }

    public class CustomFieldCollection : List<CustomField>
    {
    }

    ... etc. for all classes

    }

    ---------------------------

    If I am correct, this will allow the current code to work correctly, as the collection class names are unchanged, but, is there a better way?
  • Re: Data Access

    03-23-2005, 10:38 AM
    • Participant
      873 point Participant
    • swalther
    • Member since 08-04-2002, 10:55 AM
    • Seattle, WA
    • Posts 173
    • AspNetTeam
    Yes, you are absolutely correct! Generics in the .NET Framework 2.0 eliminate the need for all of those strongly typed collections in the .NET 1.1 version of the framework. If the Issue Tracker was rewritten for .NET 2.0, generics would be used everywhere.

    -- best,
    Stephen Walther
  • Re: Data Access

    04-02-2005, 6:02 PM
    • Member
      50 point Member
    • mmarksbury
    • Member since 11-11-2004, 12:37 AM
    • Posts 12

    Awesome.  I've been using Generics in some coding in ASP.NET 2.0.  Again, Awesome!  But I am having some trouble understanding another aspect, related to the Data Access Layer.

    I am attempting to re-create the Data Provider model that Issue Tracker uses, in my ASP.NET 2.0 application.  I am using the abstract pass-through methods in the Data Access Layer class, and using a delegate from that class to generate collections from within the individual data classes (i.e. SqlDataAccessLayer, AccessDataAccessLayer, etc.). 

    My question becomes, what is the best way to implement generics in this process, over the strong typed collections.

    Assuming this is for a category, I have done this, and would like to know if I am going about it incorrectly.

    sample from DataAccessLayer.cs

    -------------------------------------------------------------------------------------

    protected delegate IList GenerateCollectionFromReader(IDataReader returnedCollection);

    protected List<Category> GenerateCategoryCollectionFromReader(IDataReader returnData)
    {
       
    List<Category> TempCollection = new List<Category>();
       
    while(returnData.Read())
       {
             
    Category TempCategory = new Category(Convert.ToInt16(returnData["Id"]),  
                   
    Convert.ToString(returnData["Title"]))
             
             
    TempCollection.Add(TempCategory);
       }
       return TempCollection;
    }
     

    ------------------------------------------------------------------------------------

    Am I right in assuming that when the delegate's return type is of IList, I am forcing the compiler to do type conversions? 

    Any suggestions would be greatly appreciated.  Thanks.

  • Re: Data Access

    09-26-2008, 11:58 AM
    • Member
      25 point Member
    • marcschluper
    • Member since 09-18-2003, 4:39 PM
    • Posts 15

    Why would you write

    while(returnData.Read())
       {
             
    Category TempCategory = new Category(Convert.ToInt16(returnData["Id"]),  
                   
    Convert.ToString(returnData["Title"]))
             
             
    TempCollection.Add(TempCategory);
       }

    if you could write

    int ciId = returnData.GetOrdinal("Id");
    int ciTitle = returnData.GetOrdinal("Title");
    while(returnData.Read())
    {
         Category TempCategory =
    new Category(returnData.GetInt16(ciId), returnData.GetString(ciTitle));
         TempCollection.Add(TempCategory);
    }

    This would save you needless type conversions and it would reduce the number of string comparisons dramatically: the column index numbers are determined only once, not for each record.
     

     

     

    Marc Schluper
    Filed under:
Page 1 of 1 (9 items)