3

I have a method that converts List<T> to the DataTable using reflection. I want to leverage that method for creating DataSet by passing multiple lists where each list may hold different Type of object.

Below is the code that gives me compile time error:

"The type arguments for method 'ExportToExcel.CreateExcelFile.ListToDataTable<T>(System.Collections.Generic.List<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. " - at the line "var dt = Util.ListToDataTable(item);"

public static class Util
    {
        public static DataSet GetDataSet(List<string> title, List<IList> data)
        {
            DataSet ds = new DataSet();
            int idx= 0;
            foreach (var item in data)
            {
                //here I get compile time error "The type arguments for method
                // 'ExportToExcel.CreateExcelFile.ListToDataTable<T>
                // (System.Collections.Generic.List<T>)' cannot be inferred from the usage.
                // Try specifying the type arguments explicitly. "
                var dt = Util.ListToDataTable(item);
                if (title.Count >= idx)
                {
                    dt.TableName = title[idx];
                }
                idx++;
                ds.Tables.Add(dt);
            }
            return ds;
        }

        public static System.Data.DataTable ListToDataTable<T>(List<T> list)
        {
            var dt = new System.Data.DataTable();
            foreach (PropertyInfo info in typeof(T).GetProperties())
            {
                dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
            foreach (T t in list)
            {
                DataRow row = dt.NewRow();
                foreach (PropertyInfo info in typeof(T).GetProperties())
                {
                    row[info.Name] = info.GetValue(t, null);
                }
                dt.Rows.Add(row);
            }
            return dt;
        }

    }

And this is my calling testMethod

       [TestMethod]
        public void TestDataSetGeneration_WithMultipleLists()
        {
            IList list = new List<User>();
            list.Add(new User(){FirstName = "Mahesh", LastName = "Chaudhari", IsExternal = true, UpdatedOn = DateTime.Now});
            list.Add(new User(){FirstName = "Mahesh1",LastName = "Chaudhari1",IsExternal = true,UpdatedOn = DateTime.Now});
            list.Add(new User(){FirstName = "Mahesh2",LastName = "Chaudhari2",IsExternal = false,UpdatedOn = DateTime.Now});

            IList hcps = new List<HCPUser>() { new HCPUser(){FirstName = "HCP1",LastName = "HCP1"}};

            var lists = new List<IList>();
            lists.Add(list);
            lists.Add(hcps);

            var titles = new List<String> { "Users", "HCPs"};
            var result = Util.GetDataSet(titles ,lists );
            Assert.IsTrue(result != null);
        }

I think the method Util.ListToDataTable needs specific type which it gets only at runtime. In such scenario how would I call this method?

varsha
  • 1,620
  • 1
  • 16
  • 29
Mahesh
  • 982
  • 8
  • 20

2 Answers2

3

You can construct a generic MethodInfo object using MethodInfo.MakeGenericMethod and invoke that through reflection, but I see no reason that the ListToDataTable method needs to be generic.

One option would be to change List<T> to something non-generic like IEnumerable. You would have to find out the type by getting the first element from the list, though.

Another option would be LINQ:

public static System.Data.DataTable ListToDataTable<T>( object[] list )
{
    ....
}

var dt = ListToDataTable( item.Cast<object>().ToArray() );

Both options will fail to find out the type if the list is empty. Perhaps someone else has a neater way.

Edit: I now saw that your data is a non-generic IList from the start anyway, so the type is basically already lost.

Chris
  • 5,442
  • 17
  • 30
  • agree to "but I see no reason that the ListToDataTable method needs to be generic." I could do it with IEnumerable and it worked. – Mahesh Jan 15 '15 at 14:17
1

It seems that the code is slightly more complex than needed. You end with with a generic type not equaling a generic type. What is wrong with simply having a IEnumerable as asked in the previous answer? The code below passes your unit test.

    public static DataSet GetDataSet(List<string> title, IEnumerable<IEnumerable> data)
    {
        DataSet ds = new DataSet();
        int idx = 0;
        foreach (var item in data)
        {
            //here I get compile time error "The type arguments for method
            // 'ExportToExcel.CreateExcelFile.ListToDataTable<T>
            // (System.Collections.Generic.List<T>)' cannot be inferred from the usage.
            // Try specifying the type arguments explicitly. "
            var dt = Util.ListToDataTable(item);
            if (title.Count >= idx)
            {
                dt.TableName = title[idx];
            }
            idx++;
            ds.Tables.Add(dt);
        }
        return ds;
    }

    public static System.Data.DataTable ListToDataTable(IEnumerable list)
    {
        var dt = new System.Data.DataTable();
        var itm = list.OfType<object>().FirstOrDefault();
        if (itm == null)
            return dt;
        var typeProperties = itm.GetType().GetProperties();

        foreach (PropertyInfo info in typeProperties)
        {
            dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
        }
        foreach (var t in list)
        {
            DataRow row = dt.NewRow();
            foreach (PropertyInfo info in typeProperties)
            {
                row[info.Name] = info.GetValue(t, null);
            }
            dt.Rows.Add(row);
        }
        return dt;
    }
theMayer
  • 15,456
  • 7
  • 58
  • 90