tags:

views:

169

answers:

2

I have the following method:

private JobCard PopulateObject(JobCard jc, DataRow dataRow)
{

    PropertyInfo[] proplist = jc.GetType().GetProperties();

    foreach (PropertyInfo propertyitem in proplist)
    {
        if (propertyitem.Name != "")
            if (propertyitem.PropertyType.BaseType.Namespace == "System")
            {
               propertyitem.SetValue(jc, dataRow[propertyitem.Name], null);                  
            }
            else
            {
                string typename = propertyitem.ToString().Replace("Pss.Common.Mia.", "");
                int i = typename.IndexOf("Base");
                typename = typename.Substring(0, i);
                Type type = propertyitem.PropertyType;

                switch (typename)
                {
                    case "Customer":
                        propertyitem.SetValue(jc, PopulateCustomerObject(propertyitem, dataRow, type), null);
                        break;
                    case "Meter":
                        propertyitem.SetValue(jc, PopulateMeterObject(propertyitem, dataRow, type), null);
                        break;
                    case "TimeSheet":
                        propertyitem.SetValue(jc, PopulateTimeSheetObject(propertyitem, dataRow, type), null);
                        break;
                }
            }
    }

    return jc;

}

The above method calls these:

 private Customer PopulateCustomerObject(object o, DataRow dataRow, Type type)
    {
            IDataStorable instance = (IDataStorable)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
        PropertyInfo[] proplist = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

        Customer c = new Customer();

    Guid customerGuid = new Guid(dataRow["AddressId"].ToString());
    string view = ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(type).View;

    string query = string.Format("select * from {0} where id = '{1}'", view, customerGuid);

    c = DataAccess.Retriever.Retrieve<Customer>(query);

    return c;
}


private Address PopulateAddressObject(object o, DataRow dataRow, Type type)
{
    IDataStorable instance = (IDataStorable)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
    PropertyInfo[] proplist = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

    Address a = new Address();

    Guid AddressGuid = new Guid(dataRow["PhysicalAddressId"].ToString());
    string view = ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(type).View;

    string query = string.Format("select * from {0} where id = '{1}'", view, AddressGuid);

    a = DataAccess.Retriever.Retrieve<Address>(query);
    return a;
}

private Meter PopulateMeterObject(object o, DataRow dataRow, Type type)
{

    IDataStorable instance = (IDataStorable)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
    PropertyInfo[] proplist = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

    Meter m = new Meter();

    Guid meterGuid = new Guid(dataRow["MeterId"].ToString());
    string view = ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(type).View;

    string query = string.Format("select * from {0} where id = '{1}'", view, meterGuid);

    m = DataAccess.Retriever.Retrieve<Meter>(query);
    return m;
}

which I can see would be best replaced by 1 generic method, but how?

I dont see how to replace

Customer c = new Customer();
Address a = new Address();
Meter m = new Meter();
TimeSheet t = new TimeSheet();

with 1 generic line, and also

c = DataAccess.Retriever.Retrieve<Customer>(query);
a = DataAccess.Retriever.Retrieve<Address>(query);
m = DataAccess.Retriever.Retrieve<Meter>(query);
t = DataAccess.Retriever.Retrieve<TimeSheet>(query);

I cannot change Retriever.Retrieve. It is declared as

public static T Retrieve<T>(string query)
      where T : IDataStorable
     {
      return Retrieve<T>(query, new IDbDataParameter[0], string.Empty);
     }
+1  A: 

This all seems a little obscure and complex, but to answer your question directly - in order to genericise your PopulateAddressObject function you can do something like this:

private TPopulateAddressObject(object o, DataRow dataRow, Type type, string idColumnName) where T : IDataStorable, new()
{
    IDataStorable instance = (IDataStorable)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
    PropertyInfo[] proplist = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);

    T obj = new T();

    Guid id = new Guid(dataRow[idColumnName].ToString());
    string view = ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(type).View;

    string query = string.Format("select * from {0} where id = '{1}'", view, id);

    obj = DataAccess.Retriever.Retrieve<T>(query);
    return obj;
}
cbp
Note that "instance", "proplist" and "obj" (from new T()) are all discarded without actually being used (a fault in the original code)
Marc Gravell
+1  A: 

There is a lot of things in the Populate* methods that you just don't use; for example, you don't actually use the object you spend lots of time creating...

How about adding a PrimaryKey property to [DBObjectRetrieveAttribute] (to hold the mapped DataRow column), and something like:

private static T Populate<T>(DataRow dataRow)
     where T : class, IDataStorable, new()
{
    DBObjectRetrieveAttribute ora =
        ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(typeof(T));
    string view = ora.View;
    Guid pkid = new Guid(dataRow[ora.PrimaryKey].ToString());
    // beware SQL injection...
    string query = string.Format("select * from {0} where id = '{1}'",
         view, pkid);

    return DataAccess.Retriever.Retrieve<T>(query);
}

Then there is no need to switch on the different property types; you can use MakeGenericMethod:

object obj = MethodInfo mtd = typeof(SomeType).GetMethod("Populate",
       BindingFlags.NonPublic | BindingFlags.Static)
     .MakeGenericMethod(propertyitem.PropertyType)
     .Invoke(null, new object[] {dataRow});
propertyitem.SetValue(jc, obj, null);

Alternatively; pass the id in as an argument:

private static T Populate<T>(DataRow dataRow, string primaryKey)
     where T : class, IDataStorable, new()
{
    ... snip
    Guid pkid = new Guid(dataRow[primaryKey].ToString());
    ... snip
}

And do something like:

object obj;
if(type == typeof(Customer)) {
    obj = Populate<Customer>(dataRow, "AddressId");
} else if (type == typeof(Meter)) {
    obj = Populate<Meter>(dataRow, "MeterId");
} else if (...etc...) {

} else {
    throw new InvalidOperationException("Type is not supported: " + type.Name);
}
propertyitem.SetValue(jc, obj, null);
Marc Gravell
DBObjectRetrieveAttribute ora = ReflectionHelper.GetAttribute<DBObjectRetrieveAttribute>(typeof(T)); does not work.:(T is not same as propertyitem.PropertyTypeT Name = "Customer" but propertyitem.PropertyType Name = "CustomerBase'1"
callisto
Oh right; but something along those lines, then. Since CustomerBase1 wasn't mentioned, I couldn't predict that!
Marc Gravell