interfacedotnetnukedotnetnuke-5

How to properly implement IHydratable interface in DNN?


After upgrading to DNN 5.5.0 we had to implement IHydratable on all of our business objects.

This idea seemed like a good way to go at first, but after playing with IHydratable I'm not so sure any more.

There are two possibilities:

  1. I'm doing it wrong
  2. IHydratable forces you to use select * construct an all your queries

The business case:

My IHydratable is implemented as show below:

public class Bug : IHydratable
{ 
  public int BgId { get; set; }
  public string BgShortDesc { get; set; }
  public int BgReportedUser { get; set; }
  public DateTime BgReportedDate { get; set; }

  public Bug() { }

  public int KeyID
  {
    get { return BgId; }
    set { BgId = value; }
  }

  public void Fill(IDataReader dr)
  {
    BgId = Convert.ToInt32(Null.SetNull(dr["BgId"], BgId));
    BgShortDesc = Convert.ToString(Null.SetNull(dr["BgShortDesc"], BgShortDesc));
    BgReportedUser = Convert.ToInt32(Null.SetNull(dr["BgReportedUser"], BgReportedUser));
    BgReportedDate = Convert.ToDateTime(Null.SetNull(dr["BgReportedDate"], BgReportedDate));
  }
}

The fill method will throw an IndexOutOfRangeException on any of the above sprocs, since not all the fields get returned with IDataReader.

The easy way around the problem is to use select * in all of the sprocs, but that's not a good practice.

What's the PROPER way of implementing IHydratable in this scenario?

P.S. keep in mind that my example is oversimplified to get the point across.


Solution

  • I got a feasible answer to this on another forum

    Here's the suggestion:

        public void Fill(IDataReader dr)
        {
                if (dr.ColumnExists("BgId"))
                {
                  BgId = Convert.ToInt32(Null.SetNull(dr["BgId"], BgId));
                }
                //do the above for all the properties
        }
    

    EDIT:

    Found a better way of doing this by writing an extension method on IDataReader with the help of these 2 answers on SO (@JamesEggers and @Chad Grant)

        /// <summary>
        /// Check if the column exists in the datareader before accessing its value
        /// </summary>
        /// <param name="reader">DataReader</param>
        /// <param name="columnName">Column name</param>
        /// <returns>True if column exists, false if not</returns>
        public static bool ColumnExists(this IDataReader reader, string columnName)
        {
            for (int i = 0; i < reader.FieldCount; i++)
            {
                if (reader.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                {
                    return true;
                }
            }
    
            return false;
        }