10

at present, I have:

        string outputRow = string.Empty;
        foreach (var entityObject in entityObjects)
        {
            outputRow = entityObject.field1 + "," + entityObject.Field2  etc....
        }

I'm still new to the Entity Framework, is there a quicker way?

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
KevinDeus
  • 11,988
  • 20
  • 65
  • 97
  • It looks like the answers all use reflection. I'd be curious of any way to do this without reflection? I'm guessing it might not be possible though. – Brady Moritz Jan 21 '13 at 19:36
  • You can try my very light weight delimited file writer: https://gist.github.com/eranbetzalel/5371817#file-delimitedfilewriter-cs – Eran Betzalel Apr 12 '13 at 13:19

4 Answers4

27

Sample code that shows a simple yet powerful way of accomplishing what you want with no need to hard code property names (using reflection):

 /// <summary>
 /// Creates a comma delimeted string of all the objects property values names.
 /// </summary>
 /// <param name="obj">object.</param>
 /// <returns>string.</returns>
 public static string ObjectToCsvData(object obj)
 {
     if (obj == null)
     {
         throw new ArgumentNullException("obj", "Value can not be null or Nothing!");
     }
  
     StringBuilder sb = new StringBuilder();
     Type t = obj.GetType();
     PropertyInfo[] pi = t.GetProperties();
  
     for (int index = 0; index < pi.Length; index++)
     {
         sb.Append(pi[index].GetValue(obj, null));
  
         if (index < pi.Length - 1)
         {
            sb.Append(",");
         }
     }
  
     return sb.ToString();
 }

More on this:

Objects to CSV

How can i convert a list of objects to csv

Are there any CSV readers/writer lib’s in c#

Writing a CSV file in .net

LINQ to CSV : Getting data the way you want

LINQ to CSV library

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
  • Will fail if any of your properties contains a , – Sam Apr 17 '19 at 15:03
  • @Sam That's true, but if you just add a .Replace(",", "&comma;") or similar construct to the model you'll be fine. Even replacing with chr(",") will do the trick. – Brandon Osborne Feb 06 '21 at 13:45
6

I took Leniel's suggestion and wrapped it up in a full featured "writer" that also allows you to filter the properties you want written. Here's the code for your usage:

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());
    }

    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);
        return (from propertyName in propertyNames where propertyInfos.ContainsKey(propertyName) select propertyInfos[propertyName]).ToList();
    }

    private static string CsvDataFor(object obj, IList<PropertyInfo> propertyInfos)
    {
        if (obj == null)
            return "";

        var builder = new StringBuilder();

        for (var i = 0; i < propertyInfos.Count; i++)
        {
            builder.Append(propertyInfos[i].GetValue(obj, null));

            if (i < propertyInfos.Count - 1)
                builder.Append(",");
        }

        return builder.ToString();
    }
}
mBria
  • 2,412
  • 2
  • 15
  • 11
0
string csv = "";
//get property names from the first object using reflection    
IEnumerable<PropertyInfo> props = entityObjects.First().GetType().GetProperties();

//header 
csv += String.Join(", ",props.Select(prop => prop.Name)) + "\r\n";

//rows
foreach(var entityObject in entityObjects) 
{ 
    csv += String.Join(", ", props.Select(
        prop => ( prop.GetValue(entityObject, null) ?? "" ).ToString() 
    ) )
    + "\r\n";
}
  • Would be better to use StringBuilder for lots of entitys
  • The code doesn't check for when entityObjects is empty
wal5hy
  • 654
  • 6
  • 8
0

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);
        }
}
Enkode
  • 4,515
  • 4
  • 35
  • 50