locked
Casting When converting Lists in C#

    Question

  • I have the following code. I am trying to convert a List<DataRow> to List<T>, but it is giving me a runtime error saying unable to cast.

    DataSet ds = new DataSet();

    adaptor.Fill(ds);

    List<DataRow> DataRowList = (from r in ds.Tables[0].AsEnumerable() select r).ToList();

    List<T> result = DataRowList.Cast<T>().ToList<T>();   // gives an error unable to Cast

    Wednesday, February 09, 2011 11:52 AM

Answers

  • If you add this the error you are getting will go away:

    public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd )where T : class, new()

    BUT:

    Even then you will just be creating a list of empty T's.  You need to include some logic in the Select to set the values of T.  I would suggest you create an interface, something like IDataRowLoadable (or whatever you want to call it) which should have at least one method LoadFromDataRow(DataRow dr).  Then change your method signature to:

    public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd ) where T : IDataRowLoadable

    Then change the code to get your result to something like:

    var result = ds.Tables[0].AsEnumerable().Select(dr =>
            {
              var t = System.Activator<T>();
              t.LoadFromDataRow(dr);
              return (t);
            }).ToList();
    
    I haven't tested any of that, but I think it gets you closer to what you want.
    Wednesday, February 09, 2011 2:08 PM
  • The code below works well. I am not sure if that is the most efficient way to convert from Dataset to List<T>, but I was able to do this using .net 4.0.

     public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd )where T : class, new()
            {
                string tableName = query.Context.Mapping.GetTable(typeof(T)).TableName;
                List<T> result = HttpContext.Current.Cache[tableName] as List<T>;

                if (result == null)
                {
                    using (SqlConnection cn = new SqlConnection(Settings.ConnectionString))
                    {
                        cn.Open();
                        cmd.Connection = cn;
                        cmd.CommandText = StoredProcName;
                        cmd.CommandType = CommandType.StoredProcedure;
                        cmd.Notification = null;
                        cmd.NotificationAutoEnlist = true;

                        SqlCacheDependencyAdmin.EnableNotifications(Settings.ConnectionString);

                    
                        SqlCacheDependency dependency = new SqlCacheDependency(cmd);
                        SqlDataAdapter adp = new SqlDataAdapter();
                        adp.SelectCommand = cmd;
                        DataSet ds = new DataSet();
                            adp.Fill(ds);
                            adp.Dispose();
                        IList <T>  result2 = ConvertToList<T>(ds.Tables[0]);
                        result = result2.ToList<T>();
                        HttpContext.Current.Cache.Insert(tableName, result, dependency);
                    }
                }
                return result;
            }

     


            public static IList<T> ConvertToList<T>(DataTable dt) where T : class, new()
            {
                if (dt == null || dt.Rows.Count == 0) return null;
                IList<T> list = new List<T>();
                foreach (DataRow row in dt.Rows)
                {
                    T obj = ConvertDataRowToEntity<T>(row);
                    list.Add(obj);
                }
                return list;
            }

            /// <summary>
            /// Convert a single DataRow into an object of type T.
            /// </summary>
            public static T ConvertDataRowToEntity<T>(DataRow row) where T : class, new()
            {
                Type objType = typeof(T);
                T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
                foreach (DataColumn column in row.Table.Columns)
                {
                    //may error if no match
                    PropertyInfo property =
                        objType.GetProperty(column.ColumnName,
                        BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (property == null || !property.CanWrite)
                    {
                        //Debug.WriteLine("//Property " + column.ColumnName + " not in object");
                        continue; //or throw
                    }
                    object value = row[column.ColumnName];
                    if (value == DBNull.Value) value = null;
                    property.SetValue(obj, value, null);

                }
                return obj;
            }
       

    Wednesday, February 09, 2011 2:51 PM

All replies

  • Cast method does not allow you to convert the data from one type to another. It simply allows you to cast to a specific type from a generic list. Look at the example here..

    http://msdn.microsoft.com/en-us/library/bb341406.aspx


    Life would have been much easier if I had the source-code !!
    Wednesday, February 09, 2011 12:24 PM
  • Do you have any idea how can I approach my problem and convert that dataset I have into a List<T>? Do you have any sample code for that particular example?
    Wednesday, February 09, 2011 12:31 PM
  • What is the Type of 'T'?  To what other type are you trying to cast DataRow?  You can use a select to change types with logic

    var newList = dataRowList.Select(dr => new T()).ToList();

    HTH,

    Patrick

    Wednesday, February 09, 2011 1:07 PM
  • Just do it like,

    List<T> result = DataRowList as List<T>
    

    But,

    after this don' forget to check whether result is null or not.


    Please mark this post as answer if it solved your problem. Happy Programming !!!
    Wednesday, February 09, 2011 1:20 PM
  • Thanks for the comment Patrick. T is actually a name of a class in my dbml file. I do not wish to put the class name explicitly, since I am creating a generic method that accepts Table<T> as a parameter. When I put new T()? It gives me a compile error that Type T does not have the new constraint.

     

     

    Wednesday, February 09, 2011 1:44 PM
  • In your method declaration you need to add:

    somereturn something<T>(someparams) where T : new()

    Sorry, it's hard when you only provided a few lines to know the context of what you are trying to do.

    Wednesday, February 09, 2011 1:55 PM
  • Here is my code:

     

     public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd )where T : class
            {
                string tableName = query.Context.Mapping.GetTable(typeof(T)).TableName;
                List<T> result = HttpContext.Current.Cache[tableName] as List<T>;

                if (result == null)
                {
                    using (SqlConnection cn = new SqlConnection(Settings.ConnectionString))
                    {
                        cn.Open();
                        cmd.Connection = cn;
                        cmd.CommandText = StoredProcName;
                        cmd.CommandType = CommandType.StoredProcedure;
                        cmd.Notification = null;
                        cmd.NotificationAutoEnlist = true;

                        SqlCacheDependencyAdmin.EnableNotifications(Settings.ConnectionString);

                      
                      
                      SqlCacheDependency dependency = new SqlCacheDependency(cmd);
                        SqlDataAdapter adp = new SqlDataAdapter();
                        adp.SelectCommand = cmd;
                        DataSet ds = new DataSet();
                            adp.Fill(ds);
                            adp.Dispose();
                            List<DataRow> DataRowList = (from r in ds.Tables[0].AsEnumerable() select r).ToList();
                       
                            result = DataRowList.Select(dr =>  new T()).ToList();     //Error is happening here
                     
                           HttpContext.Current.Cache.Insert(tableName, result, dependency);
                    }
                }
                return result;
            }

     

     

    Wednesday, February 09, 2011 2:00 PM
  • If you add this the error you are getting will go away:

    public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd )where T : class, new()

    BUT:

    Even then you will just be creating a list of empty T's.  You need to include some logic in the Select to set the values of T.  I would suggest you create an interface, something like IDataRowLoadable (or whatever you want to call it) which should have at least one method LoadFromDataRow(DataRow dr).  Then change your method signature to:

    public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd ) where T : IDataRowLoadable

    Then change the code to get your result to something like:

    var result = ds.Tables[0].AsEnumerable().Select(dr =>
            {
              var t = System.Activator<T>();
              t.LoadFromDataRow(dr);
              return (t);
            }).ToList();
    
    I haven't tested any of that, but I think it gets you closer to what you want.
    Wednesday, February 09, 2011 2:08 PM
  • The code below works well. I am not sure if that is the most efficient way to convert from Dataset to List<T>, but I was able to do this using .net 4.0.

     public static List<T> LinqCacheStoredProc<T>(this Table<T> query, string StoredProcName,SqlCommand cmd )where T : class, new()
            {
                string tableName = query.Context.Mapping.GetTable(typeof(T)).TableName;
                List<T> result = HttpContext.Current.Cache[tableName] as List<T>;

                if (result == null)
                {
                    using (SqlConnection cn = new SqlConnection(Settings.ConnectionString))
                    {
                        cn.Open();
                        cmd.Connection = cn;
                        cmd.CommandText = StoredProcName;
                        cmd.CommandType = CommandType.StoredProcedure;
                        cmd.Notification = null;
                        cmd.NotificationAutoEnlist = true;

                        SqlCacheDependencyAdmin.EnableNotifications(Settings.ConnectionString);

                    
                        SqlCacheDependency dependency = new SqlCacheDependency(cmd);
                        SqlDataAdapter adp = new SqlDataAdapter();
                        adp.SelectCommand = cmd;
                        DataSet ds = new DataSet();
                            adp.Fill(ds);
                            adp.Dispose();
                        IList <T>  result2 = ConvertToList<T>(ds.Tables[0]);
                        result = result2.ToList<T>();
                        HttpContext.Current.Cache.Insert(tableName, result, dependency);
                    }
                }
                return result;
            }

     


            public static IList<T> ConvertToList<T>(DataTable dt) where T : class, new()
            {
                if (dt == null || dt.Rows.Count == 0) return null;
                IList<T> list = new List<T>();
                foreach (DataRow row in dt.Rows)
                {
                    T obj = ConvertDataRowToEntity<T>(row);
                    list.Add(obj);
                }
                return list;
            }

            /// <summary>
            /// Convert a single DataRow into an object of type T.
            /// </summary>
            public static T ConvertDataRowToEntity<T>(DataRow row) where T : class, new()
            {
                Type objType = typeof(T);
                T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
                foreach (DataColumn column in row.Table.Columns)
                {
                    //may error if no match
                    PropertyInfo property =
                        objType.GetProperty(column.ColumnName,
                        BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                    if (property == null || !property.CanWrite)
                    {
                        //Debug.WriteLine("//Property " + column.ColumnName + " not in object");
                        continue; //or throw
                    }
                    object value = row[column.ColumnName];
                    if (value == DBNull.Value) value = null;
                    property.SetValue(obj, value, null);

                }
                return obj;
            }
       

    Wednesday, February 09, 2011 2:51 PM
  • sorry I know this thread has some age, I think this is exactly what I need but I am not sure I understand the utilization of the methods supplied.  I am new to c# so if you can provide some additional details for using the code it would help me.  I am creating a wcf service and i want to return a list collections not data sets. thank you.
    Thursday, March 22, 2012 7:07 PM
  • I suggest the Learning Center http://msdn.microsoft.com/en-us/vstudio/hh341490 . Your new to C# :

    "Discover a wealth of resources for learning Visual C#, for both the beginner and the experienced developer."

    Provide some additional details for the code your using in a new thread.

     The questions code and answers will help the Community and users.

     
    Tuesday, April 03, 2012 10:07 AM