Search code examples
c#oopgenericstype-constraints

Converting a DataRow to a single object - type constraints


I have the following implementation that works for converting rows in a DataTable to a List<T>. The IDataItem interface just contains the SetData method that takes in a single DataRow object.

public class GroupedData<T> : List<T> where T : IDataItem, new()
    {
        public GroupedData(DataTable table)
        {
            for (int i = 0; i < table.Rows.Count; i++)
            {
                DataRow row = table.Rows[i];
                var newItem = new T();
                newItem.SetData(row);
                Add(newItem);
            }
        }
    }

These are the properties in the class I am trying to assign values to:

  public class Details
  {
    public List<AuditData> AuditEntries { get; set; }

    public AuditMethodSummaryData AuditMethodSummaryData { get; set; }
  }

With the GroupedData class, I can execute the following to populate AuditEntries:

var returnData = new Details();
returnData.AuditEntries = new GroupedData<AuditData>(dataSet.Tables[TableNames.AuditDetails]);

For the second property, AuditMethodSummaryData. I need to assign a single object instead of a List. I can't figure out how to create another class that can perform the function.

Note - I still need to pass in a DataTable. However, the data table will only ever contain a single row (due to the way the system works).

The only solution I've came up with is below. I just want to see if there is a better solution:

public class SingleData
  {
    public static T GetItem<T>(DataTable table) where T : IDataItem, new()
    {
      if (table.Rows.Count >= 1)
      {
        DataRow row = table.Rows[0];
        var newItem = new T();
        newItem.SetData(row);
      }
    }
  }

What I don't like is that i've moved the constraint from the class to the method. Is there a way I can keep it on the class? Avoiding the static method.


Solution

  • Well, for the single item you don't need generics at all:

    public class SingleItem : IDataItem
    {
      public SingleItem(DataTable table)
      {
        if (table.Rows.Count >= 1)
        {
          var row = table.Rows[0];
          SetData(row);
        }
        else
        {
          throw new ArgumentException("No rows.", "table");
        }
      }
    
      public abstract void SetData(DataRow row);
    }
    
    public AuditMethodSummaryData : SingleItem
    {
      public AuditMethodSummaryData(DataTable table) : base(table)
      {}
      public override void SetData(DataRow row) { /*...*/ }
    }
    

    This still doesn't solve case when you need to return null for zero rows. In that case you have to use factory pattern - which can be implemented in various ways - static method is the easy and I think decent approach for your case; if you don't want to have static method you have to create factory class instead like:

    public class DataItemFactory<T> where T : IDataItem, new()
    {
      private readonly DataTable m_Table;
    
      public DataItemFactory(DataTable table)
      {
        m_Table = table;
      }
    
      public T Create()
      {
        T result = default(T);
        if (m_Table.Rows.Count > 1)
        {
          result = new T();
          result.SetData(m_Table.Rows[0]);
        }
        return result;
      }
    }
    

    With usage like:

    returnData.AuditMethodSummaryData =
      new DataItemFactory<AuditMethodSummaryData>(table).Create();