Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
4.50/5 (2 votes)
See more:
Hey all,

I've been battling with this issue for some time and can't find a solution. Please help!

What I'm trying to do is dynamically extract a list of objects from a dataset using generics. It works up to a point and where it falls over is when you try and initialize an int / decimal / double. Because the default value is 0, this can be interpreted as either of the numeric types. When I run this and try to initialize an int, the compiler recognises it as a decimal and throws an exception. I've tried a number of work arounds including using the item.PropertyType.TypeInitializer.Invoke(null); The TypeInitializer is null :-( Along the same lines using the .GetConstructor() method on the PropertyType also yields no results. I've also tried to use default() but it does not accept the Type as an initializer. Pulling my hair out trying to figure this one out...

I believe the problem is using object as the Type passed to the ConvertDBNull<>() method. Not sure how to get around this as you cannot send through the PropertyType as a generic parameter.

The code is below:

C#
/// <summary>
        /// Checks if the value passed in is null. If it is, it returns the replacement value
        /// else it will convert the object value in the specified type
        /// </summary>
        /// <typeparam name="T">Type of object to be returned</typeparam>
        /// <param name="value">Value to be checked and converted</param>
        /// <param name="replacement">Replacement value in case of null</param>
        /// <returns>Value as specified type or replacement value if the object is null</returns>
        public static T ConvertDBNull<T>(object value, T replacement)
        {
            if ((value == null) || (value == DBNull.Value))
                return replacement;
            else
                return (T)Convert.ChangeType(value, typeof(T));
        }

        public static object InitializeProperty(Type propertyType)
        {
            if (propertyType == typeof(string))
                return string.Empty;
            else if ((propertyType == typeof(int)) || (propertyType == typeof(long)))
                return int.Parse("0");
            else if (propertyType == typeof(DateTime))
                return DateTime.MinValue;
            else if (propertyType == typeof(char))
                return char.MinValue;
            else if (propertyType == typeof(bool))
                return false;
            else if (propertyType == typeof(Guid))
                return new Guid();
            else if (propertyType == typeof(decimal))
                return decimal.Parse("0");
            else if (propertyType == typeof(double))
                return double.Parse("0");
            else if (propertyType.IsGenericType &&
                propertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                return null;

            return new object();
        }

        public static List<DataObj> ExtractObjectsFromDataSet<DataObj>(DataSet dsValues)
        {
            var workingItem = (DataObj)Activator.CreateInstance(typeof(DataObj));
            List<DataObj> result = new List<DataObj>();
            List<PropertyInfo> props = new List<PropertyInfo>(typeof(DataObj).GetProperties());

            foreach (DataRow row in dsValues.Tables[0].Rows)
            {
                workingItem = (DataObj)Activator.CreateInstance(typeof(DataObj));

                foreach (PropertyInfo item in props)
                {
                    if (dsValues.Tables[0].Columns.Contains(item.Name))
                    {
                        if ((row[item.Name] != DBNull.Value) && (row[item.Name] != null))
                        {
                            var tmpValue = Activator.CreateInstance(item.PropertyType);
                            item.SetValue(workingItem, ConvertDBNull<object>(row[item.Name], InitializeProperty(item.PropertyType)), null);
                        }
                    }
                }

                result.Add(workingItem);
            }

            return result;
        }

Edit: Changed lang attribute of <pre> tag from xml -> c#
Posted
Updated 19-May-11 1:42am
v2

1 solution

It looks to me that the problem you're running into is your DataRow holding an Int32 and you want to assign that to a Decimal using the SetValue method of PropertyInfo.

To do that you only need to use the Convert.ChangeType method, it'll handle the cast for you.
Like in this example application (i've stripped DataRows and DbNulls out for simplicity, but the problem is the same):

C#
using System;
using System.Collections.Generic;
using System.Reflection;

namespace ConsoleApplication14
{
    public class MyObject
    {
        public int Int { get; set; }
        public double Double { get; set; }
        public float Float { get; set; }
        public decimal Decimal { get; set; }
        public string String { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Pretend that this is your data row;
            IDictionary<string, object> values = new Dictionary<string, object>();

            values["Int"] = 1;
            values["Double"] = 123.0f;
            values["Float"] = 120.0d;
            values["Decimal"] = 2;
            values["String"] = "0";

            MyObject o = new MyObject();
            foreach (string name in values.Keys)
            {
                PropertyInfo property = o.GetType().GetProperty(name);

                // This will fail because the 2 assigned to "Decimal" 
                // is in fact an Int32
                //property.SetValue(o, values[name], null);

                // This should work
                property.SetValue(o, Convert.ChangeType(values[name], 
                                  property.PropertyType), null);
            }
        }
    }
}


Hope this helps,
Fredrik
 
Share this answer
 
Comments
PashaW81 19-May-11 6:35am    
Thanks Fredrik, that has worked beautifully! No problems with any of the types being sent through now.

Really appreciate the help!
Fredrik Bornander 19-May-11 7:21am    
Glad I could help.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900