Answered by:
Using reflections to do Add on List<T> C#

Question
-
User74064462 posted
I am reading a file which is like
HDR …
LIN …
LIN …
LIN …
HDR …
LIN …
LIN …Now LIN corresponds to line items and belong to the HDR above.
Also, mapping for the data comes from xml which has already been deserilized. This has the mappings like which property to store the data in Header or LineItems class, how many chars to pick, if it is required or not etc.
The model to store data looks like
public class Header { public string Id { get; set; } public string Description { get; set; } public List<Item> LineItems { get; set; } }
I have a generic function which reads this data into List We have different files each having different mapping. For this reason need to use a generic function to load data before processing it.
I need help with
-
#1 in the below function - is it correct way to make sure that property LineItems is not null before adding the LineItem to it?
-
How to do #2 in below function, using reflection to add lineItem to
data[headindex-1].LineItems.Add(...)
?
public static List<H> ReadFile<H, I>(ConfigTypeUploadXml uploadConfig, string fileNamePath, ref string errMsg) where H : new() where I : new() { // we'll use reflections to add LineItems to data var properties = typeof(H).GetProperties(); // read the file line by line and add to data. var data = new List<H>(); var headIndex = 0; var lineIndex = 1; foreach (string line in File.ReadAllLines(fileNamePath)) { // read HDR line if (line.StartsWith("HDR")) { var header = ReadHeaderLine<H>(uploadConfig, line, errMsg); data.Add(header); headIndex += 1; } // read LIN line if (line.StartsWith("LIN")) { var lineItem = ReadItemLine<I>(uploadConfig, line, errMsg); foreach (PropertyInfo p in properties) { if (p.Name != "LineItems") continue; //1) if items is null then create the object List<I> items = p.GetValue(data[headIndex - 1], null); if (items == null) p.SetValue(data[headIndex - 1], new List<I>()); //2) add line item to data[headIndex - 1] } } lineIndex += 1; } return data; }
Tuesday, June 11, 2019 11:08 PM -
Answers
-
User74064462 posted
Hi @Yongqing Yu,
I am reading text file line by line so i don't have the list of LineItems. I have only have lineItem that i need to add to the data[headIndex - 1].LineItems.Add(lineItem).
So had changed the function a little bit... This isn't tested since i am waiting on some additional info. The actual app is in vb.net so converting the code didn't follow the best practices so plz ignore that. For simplicity, i have removed validations as well.
public static List<THeader> ReadUpload850File<THeader, TItem>(ConfigTypeUploadXml uploadConfig, string fileNamePath, ref string errMsg) where THeader : new() where TItem : new() { // we'll use reflections to add LineItems to data PropertyInfo[] properties = typeof(THeader).GetProperties(); // read the file line by line List<THeader> data = new List<THeader>(); int headIndex = 0; int lineIndex = 1; foreach (string line in File.ReadAllLines(fileNamePath)) { // read HDR line if (line.StartsWith("HDR")) { THeader header = ReadHeaderLine<THeader>(uploadConfig, line, errMsg); data.Add(header); headIndex += 1; } // read LIN line if (line.StartsWith("LIN")) { TItem lineItem = ReadItemLine<TItem>(uploadConfig, line, errMsg); THeader header = data[data.Count - 1]; PropertyInfo lineItemsProperty = header.GetType().GetProperty("LineItems"); // Dim items As Object = lineItemsProperty.GetValue(header) // If items Is Nothing Then lineItemsProperty.SetValue(header, Nothing) IList lineItems = lineItemsProperty.GetValue(header) as IList;
if(lineItems == null) lineItems = new List<TItem>(); lineItems.Add(lineItem);
lineItemProperty.SetValue(header, lineItems) } lineIndex += 1; } return data; }- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, June 12, 2019 3:56 PM
All replies
-
User665608656 posted
Hi Learning,
According to the two issues you raised, the first one which is to make sure that the LineItems attribute is not Null before adding lineItem is correct.
Second, when you confirm that LineItems are not null, you need to add lineItem to the LineItems in data[headIndex- 1].
To achieve this function, I recommend that you still use SetValue Method.
For the use of SetValue for Reflection, you could refer to the following links:
The code you could change like below:
Since your code is not comprehensive, the following attributes and data are my fiction
if (line.StartsWith("LIN")) { var lineItem = ReadItemLine<I>(uploadConfig, line, errMsg); foreach (PropertyInfo p in properties) { if (p.Name != "LineItems") continue; //1) if items is null then create the object List<I> items = p.GetValue(data[headIndex - 1], null); if (items == null)
{
//2) add line item to data[headIndex - 1]
Item dd = new Item();
dd.ItemID = "1";
dd.ItemName = "item1";
List<Item> itemlist = new List<Item>();
itemlist.Add(dd);
p.SetValue(data[headIndex - 1], itemlist);
} } }Here is the debug picture of the data[headIndex - 1]:
Best Regards,
YongQing.
Wednesday, June 12, 2019 5:36 AM -
User74064462 posted
Hi @Yongqing Yu,
I am reading text file line by line so i don't have the list of LineItems. I have only have lineItem that i need to add to the data[headIndex - 1].LineItems.Add(lineItem).
So had changed the function a little bit... This isn't tested since i am waiting on some additional info. The actual app is in vb.net so converting the code didn't follow the best practices so plz ignore that. For simplicity, i have removed validations as well.
public static List<THeader> ReadUpload850File<THeader, TItem>(ConfigTypeUploadXml uploadConfig, string fileNamePath, ref string errMsg) where THeader : new() where TItem : new() { // we'll use reflections to add LineItems to data PropertyInfo[] properties = typeof(THeader).GetProperties(); // read the file line by line List<THeader> data = new List<THeader>(); int headIndex = 0; int lineIndex = 1; foreach (string line in File.ReadAllLines(fileNamePath)) { // read HDR line if (line.StartsWith("HDR")) { THeader header = ReadHeaderLine<THeader>(uploadConfig, line, errMsg); data.Add(header); headIndex += 1; } // read LIN line if (line.StartsWith("LIN")) { TItem lineItem = ReadItemLine<TItem>(uploadConfig, line, errMsg); THeader header = data[data.Count - 1]; PropertyInfo lineItemsProperty = header.GetType().GetProperty("LineItems"); // Dim items As Object = lineItemsProperty.GetValue(header) // If items Is Nothing Then lineItemsProperty.SetValue(header, Nothing) IList lineItems = lineItemsProperty.GetValue(header) as IList;
if(lineItems == null) lineItems = new List<TItem>(); lineItems.Add(lineItem);
lineItemProperty.SetValue(header, lineItems) } lineIndex += 1; } return data; }- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, June 12, 2019 3:56 PM -
User665608656 posted
Hi Learning,
According to your description,I'm not clear about your requirements.
Based on your code,
IList lineItems = lineItemsProperty.GetValue(header) as IList;
What's the definition of IList in this line? It will report errors in my test.
So I change IList to List<TItem>,I guess IList refers to List<TItem>,right?
After change this code I test your codes successfully,then I convert them to vb.net code as you expected below:
Public Function ReadUpload850File(Of THeader As New, TItem As New)(ByVal fileNamePath As String, ByRef errMsg As String) As List(Of THeader) Dim properties As PropertyInfo() = GetType(THeader).GetProperties() Dim data As List(Of THeader) = New List(Of THeader)() Dim headIndex As Integer = 0 Dim lineIndex As Integer = 1 For Each line As String In File.ReadAllLines(fileNamePath) If line.StartsWith("HDR") Then Dim header As THeader = ReadHeaderLine(Of THeader)(line, errMsg) data.Add(header) headIndex += 1 End If If line.StartsWith("LIN") Then Dim lineItem As TItem = ReadHeaderLinesd(Of TItem)(line, errMsg) Dim header As THeader = data(data.Count - 1) Dim lineItemsProperty As PropertyInfo = header.[GetType]().GetProperty("LineItems") Dim lineItems As List(Of TItem) = TryCast(lineItemsProperty.GetValue(header), List(Of TItem)) If lineItems Is Nothing Then lineItems = New List(Of TItem)() lineItems.Add(lineItem) lineItemsProperty.SetValue(header, lineItems) End If lineIndex += 1 Next Return data End Function
Best Regards,
YongQing.
Thursday, June 13, 2019 7:15 AM -
Thursday, June 13, 2019 3:41 PM