Building on @mBria's code I updated it so you can supply a related table properties using dot notation:
public class CsvFileWriter
{
public static void WriteToFile<T>(string filePath, List<T> objs, string[] propertyNames)
{
var builder = new StringBuilder();
var propertyInfos = RelevantPropertyInfos<T>(propertyNames);
foreach (var obj in objs)
builder.AppendLine(CsvDataFor(obj, propertyInfos));
File.WriteAllText(filePath, builder.ToString());
}
public static void WriteToFileSingleFieldOneLine<T>(string filePath, List<T> objs, string propertyName)
{
var builder = new StringBuilder();
var propertyInfos = RelevantPropertyInfos<T>(new[] { propertyName });
for (var i = 0; i < objs.Count; i++)
{
builder.Append(CsvDataFor(objs[i], propertyInfos));
if (i < objs.Count - 1)
builder.Append(",");
}
File.WriteAllText(filePath, builder.ToString());
}
public static string GetCSVData<T>(List<T> objs, string[] propertyNames, string[] renameHeaders)
{
var propertyInfos = RelevantPropertyInfos<T>(propertyNames);
var builder = new StringBuilder();
if (renameHeaders != null && renameHeaders.Count() > 0)
builder.AppendLine("\"" + String.Join("\",\"", renameHeaders.Select(i => i.Replace("\"", "'"))) + "\"");
else
builder.AppendLine(String.Join(",", propertyInfos.Select(i => i.Name).ToList()));
foreach (var obj in objs)
builder.AppendLine(CsvDataFor(obj, propertyInfos));
return builder.ToString();
}
private static List<PropertyInfo> RelevantPropertyInfos<T>(IEnumerable<string> propertyNames)
{
var propertyInfos = typeof(T).GetProperties().Where(p => propertyNames.Contains(p.Name)).ToDictionary(pi => pi.Name, pi => pi);
propertyInfos.Remove("EntityAspect");
// Adding related objects
foreach (var property in typeof(T).GetProperties())
{
if (property.PropertyType.Namespace == "System.Collections.Generic") // if property is a collection
{
var subType = property.PropertyType.GenericTypeArguments[0]; // Get the type of items in collection
var subProperties = subType.GetProperties().Where(p => propertyNames.Contains($"{property.Name}.{p.Name}")); // Get properties of related object
foreach (var subProperty in subProperties)
{
propertyInfos.Add($"{property.Name}.{subProperty.Name}", subProperty); // Add subproperties to propertyInfos
}
}
else if (!property.PropertyType.Namespace.StartsWith("System")) // if property is an object
{
var subProperties = property.PropertyType.GetProperties().Where(p => propertyNames.Contains($"{property.Name}.{p.Name}")); // Get properties of related object
foreach (var subProperty in subProperties)
{
propertyInfos.Add($"{property.Name}.{subProperty.Name}", subProperty); // Add subproperties to propertyInfos
}
}
}
return (from propertyName in propertyNames where propertyInfos.ContainsKey(propertyName) select propertyInfos[propertyName]).ToList();
}
private static string CsvDataFor<T>(T obj, List<PropertyInfo> propertyInfos)
{
var values = new List<string>();
foreach (var propertyInfo in propertyInfos)
{
try
{
// Check if it's a nested property
if (propertyInfo.ReflectedType.Name != obj.GetType().Name)
{
var property = typeof(T).GetProperty(propertyInfo.ReflectedType.Name);
if (property != null)
{
var subProperty = property.PropertyType.GetProperty(propertyInfo.Name);
if (subProperty != null)
{
if (property.PropertyType.Namespace == "System.Collections.Generic") // if it's a collection
{
var collection = property.GetValue(obj) as IEnumerable;
if (collection != null)
{
values.Add(String.Join(";", from object item in collection select subProperty.GetValue(item)?.ToString()));
}
else
{
values.Add(""); // Add empty value if collection is null
}
}
else // if it's a single object
{
var relatedObject = property.GetValue(obj);
if (relatedObject != null)
{
values.Add(subProperty.GetValue(relatedObject)?.ToString());
}
else
{
values.Add(""); // Add empty value if related object is null
}
}
}
else
{
values.Add(""); // Add empty value if subProperty not found
}
}
else
{
values.Add(""); // Add empty value if property not found
}
}
else
{
var value = propertyInfo.GetValue(obj);
values.Add(value == null ? "" : value.ToString());
}
}
catch (Exception ex)
{
// Handle any error that occurred during getting the value.
// This block will catch any unhandled exceptions and allow the loop to continue with the next property.
// Add error handling code here as needed.
values.Add(""); // Add empty value in case of error
}
}
//Join the string representations of the values with a comma and return the result.
return string.Join(",", values);
}
}