 |
|
 |
Public Shared Function GetObject(DataRow As DataRow, Type As System.Type) As DatabaseObject
GetObject = Activator.CreateInstance(Type)
Dim PropertyInfo As System.Reflection.PropertyInfo
Dim Converter As System.ComponentModel.TypeConverter
Dim Value As Object
For Each DataColumn As DataColumn In DataRow.Table.Columns
PropertyInfo = Type.GetProperty(DataColumn.ColumnName)
Value = DataRow(DataColumn)
If Not Value.Equals(DBNull.Value) Then
Converter = System.ComponentModel.TypeDescriptor.GetConverter(PropertyInfo.PropertyType)
If Value IsNot Nothing Then
If Value.GetType IsNot PropertyInfo.PropertyType AndAlso Converter.CanConvertFrom(Value.GetType) Then
Value = Converter.ConvertFrom(Value)
Else
Select Case Value.GetType.Name
Case "DateTimeOffset"
Value = CType(Value, DateTimeOffset).LocalDateTime
End Select
End If
PropertyInfo.SetValue(GetObject, Value, Nothing)
End If
End If
Next
End Function
Thanks,
toddmo
|
|
|
|
 |
|
 |
Interesting, thanks for sharing your code. Ben
|
|
|
|
 |
|
 |
Hi Kubben
Your code is really great! It helps me a lot in programming. But i found something wrong with it when reflecting from a null datetime value to an object.
The birthday field in my db has a NULL value. But when I convert a datarow with that null field to an object. The birthday property has a "01/01/0001" value Is there a workaround it ?
|
|
|
|
 |
|
 |
You know it has been a while since I have used this code. There are some issues with nulls or dbnull from a table and an object that doesn't support null. Since you can have types in your object that are nullable types, that is what I would suggest. You may need to check to see if the database field is null before you try to set it. Ben
|
|
|
|
 |
|
 |
Thx for replying me.
Im my C# project, the birthday field's type is SqlDateTime ( since it allows null value) and in SQL Server it's type is DateTime. Is it a problem ?
|
|
|
|
 |
|
 |
The only way I have overcome this issue is with nullable types. So you need a property defined like this: private DateTime? _custDate; public DateTime? CustDate { get { return _custDate; } set { _custDate = value; } } This will allow the object to load null dates. Then you need to change the code to look for the nullable type when converting object to dataset. public static void ObjectToTableConvert(Object p_obj, ref DataSet p_ds, String p_tableName) { //we need the type to figure out the properties Type t = p_obj.GetType(); //Get the properties of our type PropertyInfo[] tmpP = t.GetProperties(); //We need to create the table if it doesn't already exist if (p_ds.Tables[p_tableName] == null) { p_ds.Tables.Add(p_tableName); //Create the columns of the table based off the properties we reflected from the type foreach (PropertyInfo xtemp2 in tmpP) { if (xtemp2.PropertyType.ToString() == "System.Nullable`1[System.DateTime]") { p_ds.Tables[p_tableName].Columns.Add(xtemp2.Name, typeof(DateTime)); } else { p_ds.Tables[p_tableName].Columns.Add(xtemp2.Name, xtemp2.PropertyType); } } //foreach } //Now the table should exist so add records to it. Object[] tmpObj = new Object[tmpP.Length]; for (Int32 i=0;i<=tmpObj.Length-1;i++) { tmpObj[i] = t.InvokeMember(tmpP[i].Name , BindingFlags.GetProperty, null, p_obj, new object[0]); } //Add the row to the table in the dataset p_ds.Tables[p_tableName].LoadDataRow(tmpObj,true); } Hope that helps. Ben
|
|
|
|
 |
|
 |
This article was very helpful to me as I was searching for a method to salesforce sobjects to an array.
|
|
|
|
 |
|
 |
After using the previous method a few times I realized that it's better to traverse the class object properties and find the matching column. Otherwise the order of the dataset columns must match the order of the properties listed in the class. If you use inheritance and composition, the order of the properties gets more complex.
Public Sub LoadObject1(ByVal p_dcc As DataColumnCollection, _
ByVal p_dr As DataRow, _
ByRef p_o As Object)
' object reflection.
Dim t As Type
t = p_o.GetType
Dim objType As Type ' the business object type.
Dim objPropertiesArray() As System.Reflection.PropertyInfo 'Business object
Dim objProperty As PropertyInfo
Dim col As DataColumn
objType = p_o.GetType
objPropertiesArray = objType.GetProperties
Try
For Each objProperty In objPropertiesArray
col = p_dcc(objProperty.Name) ' find the column that matches the current property
objProperty.SetValue(p_o, Convert.ChangeType(Trim(p_dr.Item(col)), objProperty.PropertyType), Nothing) ' set the object property value
Next
Catch ex As Exception
ex.Message.ToString()
End Try
End Sub
|
|
|
|
 |
|
 |
Thanks for your post. That is a good point. I think you are right it is probably better to do it that way. Ben
|
|
|
|
 |
|
 |
One can use the objectProperties to convert different data types to the object property.
like this:
Public Sub LoadObject(ByVal p_dcc As DataColumnCollection, _
ByVal p_dr As DataRow, _
ByRef p_o As Object)
' object reflection.
Dim t As Type
t = p_o.GetType
Dim objType As Type ' the business object type.
Dim objPropertiesArray() As System.Reflection.PropertyInfo 'Business object
Dim objProperty As PropertyInfo
Dim o(0) As String ' stores the column names. This could have been a string object.
Dim i As Int32 = Int32.MinValue
For i = 0 To p_dcc.Count - 1
Try
objType = p_o.GetType
objPropertiesArray = objType.GetProperties
objProperty = objPropertiesArray(i)
' NOTE: datarow column names must be exact matches
' To the Object property names.
o(0) = p_dr.Item(p_dcc(i).ColumnName)
objProperty.SetValue(p_o, Convert.ChangeType(Trim(o(0)), objProperty.PropertyType), Nothing)
Catch ex As Exception
' take appropriate action.
End Try
Next
End Sub
|
|
|
|
 |
|
 |
Thanks for your post. That is a useful thing to know. Ben
|
|
|
|
 |
|
 |
Thanks for doing great part of the hard work.
Cheers.
|
|
|
|
 |
|
 |
I am glad you liked the code. Happy coding.
Ben
|
|
|
|
 |
|
 |
Yes indeed!
Have you tried the same type of conversion while using code generation libraries such as CodeGen ?
Do you have any practical data about performance ?
It is one thing to know what to want, second to really want it, third to know how to do it, fourth to be skillful to do it, fifth to actually do it and last but not least to not regret after doing it
|
|
|
|
 |
|
 |
I have not really worked with CodeGen at all, so I am not sure how will it would or would not work. I really haven't done any measuring on performance since I wasn't too concerned about it running as fast as possible. Anytime you use reflection it takes longer. I would guess as long as your objects aren't too big, i.e. not too many columns in a row, that the performance is pretty good. If you have hugh objects, lots of columns, your performance is going to get hurt.
This code has been used in serveral production apps that are still in use and we haven't noticed any performance issues with it.
Ben
|
|
|
|
 |
|
 |
I had begun making a simular project not too long ago but I chose to go another route. I doubt I will post an article about what I am doing but I can suggest that you may be able to extend your idea far further.
nothing
|
|
|
|
 |
|
 |
I have made several changes to the code I have posted here. Including being able to choose which properties you want to convert a datarow. I just haven't gotten around to posting an update. It seems most people don't see the usefulness of this sort of thing. That is ok to each their own.
You might as well post your article. People will only benefit from seeing something similar in a couple of different ways.
Ben
|
|
|
|
 |
|
|
 |
|
 |
Glad you are finding it useful. Thanks for your post.
Ben
|
|
|
|
 |
|
 |
I am curious as to why you used
for (Int32 i=0;i<=p_dcc.Count -1;i++)
instead of
for (Int32 i=0;i<p_dcc.Count;i++)
The second method doesn't involve a subtraction. I was wondering if there is a hidden side effect I am missing.
Thanks,
Brian Leach
|
|
|
|
 |
|
 |
Hey Brian,
You are not missing anything. Both will work fine. Sometimes you get in a habit of doing things a certain way. It reminds you what is zero based, like the collection, and what is 1 based like the count of items in the collection. I was in a habit for a while of always subtracting one from a count, now I have gone back to just < the count. They both work.
Thanks,
Ben
|
|
|
|
 |
|
 |
Take a look at how the CurrencyManager works with databinding of properties in a class. You'll notice that navigating a data source will automatically populate the properties of a class instance, and vice versa. The whole data binding process will also do type conversion for you automatically. You might want to read my article on databinding[^], as xxxChanged events are really helpful when using the CurrencyManager and data binding.
Marc
My website
Traceract
Understanding Simple Data Binding
Diary Of A CEO - Preface
|
|
|
|
 |
|
 |
Marc,
Thanks for your comment and link to your article. In my project we had base objects and objects that inherited from those base objects. Using the methods I descibed above I don't need to know and specifically bind each property. I spin through the datarow and it finds the correct property on the base object or child object automatically. Also as there are more properties you have to create more binding statements. This code just works with out having to add anything to it. The project I was working on was all middle tier so there was no visual manifestion or form object. Still I appreate the binding approach and will keep that in mind for future projects.
Thanks again,
Ben
|
|
|
|
 |
|
 |
...but prone to a few problems. For a very simple example, consider: class Foo { int _x, _y; string _w; public int X { get { return this._x; } } public int Y { set { this._y = value }; } public static void Z () { ///code here; } public string W { get { return this._w; } set { this._w = value; } } And a DataRow that has ('W', int), ('X', int) , ('y', int ), and 'Z' columns. Your Load(...) function won't manage to assign anything to a Foo obect, and will throw four exceptions which the invoking code will know nothing about. Similarly your ObjectToTableConvert will create a table with ('W', string), ('X', int) and ('Y', int) columns, but only then bomb out because Foo.Y doesn't have a get accessor. The code represented in this article is a good starting point, but blindly calling InvokeMember is perhaps a bit too error prone, and catching, and then doing nothing with, exceptions is, IMHO, always a sign that you're going to spend hours of testing/debugging time scratching your head, wondering where the error is. -- modified at 4:13 Sunday 9th October, 2005
|
|
|
|
 |
|
 |
Thanks for your comments. Like I mentioned in the article this is a simple object to table or datarow to object method. Like you showed in your example code this would clearly not work in some cases. I think there are a number of examples where this would work well. It all depends on what your needs are. I found it extremely helpful for the project I was working on and thought I would share it with others in the hope that they might find it helpful as well.
Thanks,
Ben
|
|
|
|
 |