Export iQueryable of complex objects to Excel - c#

We have some code that exports data from a database to Excel and it works well.
The main problem is we use a DB view to gather all our data. This creates the issue of having a sizable view as we have multiple types of objects of which we export.
class Event
int id
string title
List<EventDate> Dates
string desc
class EventDate
DateTime start
DateTime end
List<EventLocation> Locations
class EventLocation
string address
string city
string zip
class Birthday : Event
int balloonsOrdered
string cakeText
class Meeting : Event
string Organizer
string Topic
So, above is the model. Birthday and Meeting inherit from Event and all Event objects have a list of EventDate objects. Each EventDate object has a start date, end date and a list of Location objects.
Our goal is to find a dynamic way to get data from the DB to an Excel doc. We'd rather not maintain a massive view in the database (as we'll add more event types eventually).
I'm not all that familiar with .NET's XML capabilities, but the solution we are using now uses OpenXML and the code makes sense.

You could create a CSV file using List<T> and and following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web;
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
using (StringWriter sw = new StringWriter())
{
//gets all properties
PropertyInfo[] props = t.GetProperties();
//this is the header row
//foreach of the properties in class above, write out properties
foreach (PropertyInfo pi in props)
{
sw.Write(pi.Name.ToUpper() + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (PropertyInfo Column in props)
{
//this is the row+col intersection (the value)
string value = item.GetType().GetProperty(Column.Name).GetValue(item, null).ToString();
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}

I improved the solution from Brad to better work with Entity Framework Data Annotations :
it gets the display name from annotations instead of the column name
it does not export columns that are marked "scaffold = false" via annotations
it offers you a way to deal with complex types (not primitive types)
public static class ExportListToCSV
{
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
//gets all columns
PropertyInfo[] columns = t.GetProperties();
// skip columns where ScaffoldColumn = false
// useful to skip column containing IDs for Foreign Keys
var props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(p => new
{
Property = p,
Attribute = p.GetCustomAttribute<ScaffoldColumnAttribute>()
})
.Where(p => p.Attribute == null || (p.Attribute != null && p.Attribute.Scaffold != false))
.ToList();
using (StringWriter sw = new StringWriter())
{
//this is the header row
foreach (var prop in props)
{
var pi = prop.Property;
string columnName = "";
// outputs raw column name, but this is not really meaningful for the end user
//sw.Write(pi.Name + ",");
columnName = pi.GetDisplayName();
sw.Write(columnName + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (var prop in props)
{
var column = prop.Property;
//this is the row+col intersection (the value)
PropertyInfo info = item.GetType().GetProperty(column.Name);
//string value = info.GetValue(item, null);
string value = GetDescriptionForComplexObjects(info.GetValue(item, null));
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
private static string GetDescriptionForComplexObjects(object value)
{
string desc = "";
if (
(value != null && !IsSimpleType(value.GetType()))
)
{
dynamic dynObject = value;
if (dynObject != null)
{
//using Model Extensions,
//I made sure all my objects have a DESCRIPTION property
//this property must return a string
//for an employee object, you would return the employee's name for example
//desc = dynObject.DESCRIPTION;
}
}
else
{
desc = "" + value;
}
return desc;
}
public static string GetDisplayName(this PropertyInfo pi)
{
if (pi == null)
{
throw new ArgumentNullException(nameof(pi));
}
return pi.IsDefined(typeof(DisplayAttribute)) ? pi.GetCustomAttribute<DisplayAttribute>().GetName() : pi.Name;
}
public static bool IsSimpleType(Type type)
{
return
type.IsPrimitive ||
new Type[] {
typeof(Enum),
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
;
}
}
}

Related

windows form application list to ADO.NET datatable

This is my first question, please ignore my mistakes.
I am running a C# project which passes requirements of twitter feed, 5 attributes in a string named "urlAll". I am going to split the properties with *#06# which I declared before which is given bellow.
public DataTable GetTwitterDataTableMulti(string urlAll)
{
Trace.bDebug = false;
if (ExitNow)
{
return null;
}
string[] allurl = urlAll.Split(new string[] { "*#06#" }, StringSplitOptions.None);
return Dt;
}
I have created another project with windows form application using LinqToTwitter and it returns a list. All I need is to return the list as a ado.net table which I have written before. Below is my twitter feed code.
//initializing a list of current news feeds
private List<Status> currentTweets;
//parent class of the form application
//interface
public TwitterFeed()
{
//initializing the components
InitializeComponent();
//call the function that will display current 200 feeds from twitter
GetMostRecent200HomeTimeLine();
//clearing the textbox in which the feeds will be displayed
lstTweetList.Items.Clear();
//displaying the feed in text box right
currentTweets.ForEach(tweet => lstTweetList.Items.Add(tweet.User.Name + ":" + tweet.Text));
//display follow list in text box left
//GetSideBarList(GetFollowers()).ForEach(name => lstFollowNames.Items.Add(name));
}
//method that gets current 200 feeds form twitter of the user
private void GetMostRecent200HomeTimeLine()
{
var twitterContext = new TwitterContext(authorizer);
var tweets = from tweet in twitterContext.Status
where tweet.Type == StatusType.Home &&
tweet.Count == 200
select tweet;
currentTweets = tweets.ToList();
}
what would be the possible way to integrate them ?
This should work if your list does not have any other collection:
public static DataTable ToDataTable<T>(this IList<T> list)
{
DataTable table = null;
if (list != null && list.Count > 0)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
List<PropertyDescriptor[]> propList = new List<PropertyDescriptor[]>();
table = new DataTable();
foreach (PropertyDescriptor item in properties)
{
if (IsArrayOrCollection(item.PropertyType))
{
continue;
}
AddColumnsForProperties(typeof(T).Namespace, null, table, (new[] { item }).ToList(), ref propList);
}
object[] values = new object[propList.Count];
foreach (T item in list)
{
for (int i = 0; i < values.Length; i++)
values[i] = GetValueFromProps(propList[i], item) ?? DBNull.Value;
table.Rows.Add(values);
}
}
return table;
}
private static object GetValueFromProps(PropertyDescriptor[] descriptors, object item)
{
var result = item;
foreach (var descriptor in descriptors)
{
if (result != null)
{
result = descriptor.GetValue(result);
}
}
return result;
}
private static void AddColumnsForProperties(string myNamespace, string parentName, DataTable dt, List<PropertyDescriptor> p, ref List<PropertyDescriptor[]> properties)
{
var pLast = p.Last();
if (pLast.PropertyType.Namespace != null && pLast.PropertyType.Namespace.StartsWith(myNamespace))
{
var allProperties = pLast.GetChildProperties();
if (allProperties.Count > 0)
{
foreach (PropertyDescriptor item in allProperties)
{
var newP = p.ToList();
newP.Add(item);
string childParentName = !String.IsNullOrEmpty(parentName)
? String.Format("{0}.{1}.{2}", parentName, pLast.Name, item.Name)
: String.Format("{0}.{1}", pLast.Name, item.Name);
if (item.PropertyType.Namespace != null && item.PropertyType.Namespace.ToLower().StartsWith(myNamespace))
{
AddColumnsForProperties(myNamespace, childParentName, dt, newP, ref properties);
}
else if (!dt.Columns.Contains(childParentName))
{
dt.Columns.Add(childParentName, Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType);
properties.Add(newP.ToArray());
}
}
}
else if (!dt.Columns.Contains(pLast.Name))
{
dt.Columns.Add(pLast.Name, Nullable.GetUnderlyingType(pLast.PropertyType) ?? pLast.PropertyType);
properties.Add(p.ToArray());
}
}
else if (!dt.Columns.Contains(pLast.Name))
{
dt.Columns.Add(pLast.Name, Nullable.GetUnderlyingType(pLast.PropertyType) ?? pLast.PropertyType);
properties.Add(p.ToArray());
}
}

MVC 5 Getting the DisplayFormatAttribute of DbEntry

I have a audit method that logs changes to my datatabase.
The method looks (abit)simplified like this
private List<Case_History> GetRecords(DbEntityEntry dbEntry, ApplicationUser user, int actionID)
{
List<Case_History> result = new List<Case_History>();
DateTime changeTime = DateTime.Now;
// Finds the table
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Finds the table name
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().BaseType.Name;
// Finds primary key
string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
else if (dbEntry.State == EntityState.Modified)
{
List<string> values = new List<string>();
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName)) && propertyName != "Modified_date")
{
//DEBUGGING VARIABLE
var originalValue = dbEntry.OriginalValues.GetValue<object>(propertyName);
//DEBUGGING VARIABLE
var newValue = dbEntry.CurrentValues.GetValue<object>(propertyName);
//Here is the part where i want to get the column display name
// This code is not working
PropertyInfo prop = typeof(this).GetProperty(propertyName);
var att = (DisplayAttribute)prop.GetCustomAttributes(typeof(DisplayAttribute);
if (att != null)
{
//found Display name
}
//If the record is different, record the change
if (dbEntry.OriginalValues.GetValue<object>(propertyName) != null && dbEntry.CurrentValues.GetValue<object>(propertyName) != null)
{
values.Add(propertyName + ": " + dbEntry.OriginalValues.GetValue<object>(propertyName).ToString() + " -> " + dbEntry.CurrentValues.GetValue<object>(propertyName).ToString());
}
}
}
}
I have found the variable in the locals in debugging session in the metadata propterties of the field. But only in the "this" variable. And this have to be dynamic for each different DBentries.
Just replace
PropertyInfo prop = typeof(this).GetProperty(propertyName);
by
PropertyInfo prop = dbEntry.Entity.GetType().GetProperty(propertyName);
Indeed, "this" is the current class, it contains no properties from the entity you want to log

Convert datatable to list of generic type

How to convert datatable into list of generic type. Below is the scenario.
I have datatable with name table1 and contains columns col1,col2. how could we convert this table into a list of type name table1bj(which can be different per the datatable name) with properties col1 and col2 with compatible datatype as of datatable column data types.
There are many post on SO but these are with the converting datatable into predefined object list. Here in my case I have to generate object and list dynamically from the datatable. Thanks.
Assuming that you've already created the class table1bj (consider to make it uppercase due to .NET naming conventions) with two properties col1,col2 (the same). You just have to use Enumerable.Select to create instances of this class and ToList to create a generic List<table1bj>:
List<table1bj> result = table1.AsEnumerable()
.Select(row => new table1bj
{
col1 = row.Field<string>("col1"),
col1 = row.Field<string>("col1")
}
).ToList();
I have also presumed that these properties are strings, otherwise use the correct type with the Field extension method. If you don't know the type you should stay with your DataTable since it's already an in-memory collection with dynamic types.
You can do like this...
Create Class with properties :
public class table1bj
{
public string col1{ get; set; }
public string col2{ get; set; }
}
Convert DataTable to Generic Type :
List<table1bj> Objtable1bj = table1.ToCollection<table1bj>();
I know this question asked many times ago, but also I need a solutions for convert data table to dynamic or generic types in one method and I can't find answer for this, so post my answer.
You can use a extension method to convert data table to any type like below:
public static class Extension
{
public static IList<T> ToList<T>(this DataTable dt, bool isFirstRowColumnsHeader = false) where T : new()
{
var results = new List<T>();
if (dt != null && dt.Rows.Count > 0)
{
var columns = dt.Columns.Cast<DataColumn>().ToList();
var rows = dt.Rows.Cast<DataRow>().ToList();
var headerNames = columns.Select(col => col.ColumnName).ToList();
//
// Find properties name or columns name
if (isFirstRowColumnsHeader)
{
for (var i = 0; i < headerNames.Count; i++)
{
if (rows[0][i] != DBNull.Value && !string.IsNullOrEmpty(rows[0][i].ToString()))
headerNames[i] = rows[0][i].ToString();
}
//
// remove first row because that is header
rows.RemoveAt(0);
}
// Create dynamic or anonymous object for `T type
if (typeof(T) == typeof(System.Dynamic.ExpandoObject) ||
typeof(T) == typeof(System.Dynamic.DynamicObject) ||
typeof(T) == typeof(System.Object))
{
var dynamicDt = new List<dynamic>();
foreach (var row in rows)
{
dynamic dyn = new ExpandoObject();
dynamicDt.Add(dyn);
for (var i = 0; i < columns.Count; i++)
{
var dic = (IDictionary<string, object>)dyn;
dic[headerNames[i]] = row[columns[i]];
}
}
return (dynamic)dynamicDt;
}
else // other types of `T
{
var properties = typeof(T).GetProperties();
if (columns.Any() && properties.Any())
{
foreach (var row in rows)
{
var entity = new T();
for (var i = 0; i < columns.Count; i++)
{
if (!row.IsNull(columns[i]))
{
typeof(T).GetProperty(headerNames[i])? // ? -> maybe the property by name `headerNames[i]` is not exist in entity then get null!
.SetValue(entity, row[columns[i]] == DBNull.Value ? null : row[columns[i]]);
}
}
results.Add(entity);
}
}
}
}
return results;
}
}
We can do it by Reflection also, this method helps to set ClassObject properties by DataTable:
using System.Reflection;
public void SetObjectProperties(object objClass, DataTable dataTable)
{
DataRow _dataRow = dataTable.Rows[0];
Type objType = objClass.GetType();
List<PropertyInfo> propertyList = new List<PropertyInfo>(objType.GetProperties());
foreach (DataColumn dc in _dataRow.Table.Columns)
{
var _prop = propertyList.Where(a => a.Name == dc.ColumnName).Select(a => a).FirstOrDefault();
if (_prop == null) continue;
_prop.SetValue(objClass, Convert.ChangeType(_dataRow[dc], Nullable.GetUnderlyingType(_prop.PropertyType) ?? _prop.PropertyType), null);
}
}

reflection on List and printing values

I wrote a method that accepts a generic parameter and then it prints its properties. I use it to test my web service. It's working but I want to add some features that I don't know how to implement. I want to print values of lists, because now it just writes System.Collection.Generic.List1 which is expected.
Here is my code so far, this is working for basic types (int, double etc.):
static void printReturnedProperties<T>(T Object)
{
PropertyInfo[] propertyInfos = null;
propertyInfos = Object.GetType().GetProperties();
foreach (var item in propertyInfos)
Console.WriteLine(item.Name + ": " + item.GetValue(Object).ToString());
}
You could do something like this:
static void printReturnedProperties(Object o)
{
PropertyInfo[] propertyInfos = null;
propertyInfos = o.GetType().GetProperties();
foreach (var item in propertyInfos)
{
var prop = item.GetValue(o);
if(prop == null)
{
Console.WriteLine(item.Name + ": NULL");
}
else
{
Console.WriteLine(item.Name + ": " + prop.ToString());
}
if (prop is IEnumerable)
{
foreach (var listitem in prop as IEnumerable)
{
Console.WriteLine("Item: " + listitem.ToString());
}
}
}
}
It will then enumerate through any IEnumerable and print out the individual values (I'm printing them one per line, but obviously, you can do different.)
The elements inside a list can be retrieved through the indexer property Item. This property accepts an index argument (there is an overload of PropertyInfo.GetValue that accept an object array, just like MethodInfo.Invoke) and returns the object at that position.
int index = /* the index you want to get here */;
PropertyInfo indexer = Object.GetProperty("Item");
object item = indexer.GetValue(Object, new object[] { index });
I usually prints list with a , between each item.
To make that easy I have created a simple extension method:
public static class ListEx
{
public static string StringJoin<T>(this IEnumerable<T> items)
{
return string.Join(", ", items);
}
}
Call the method as myList.StringJoin().
You can of course modify the method to use another delimiter och call string.Join directly.
Here is a snippet, assuming that your List is of Type T.
foreach (PropertyInfo item in propertyInfos)
{
Object obj = item.GetValue(object,null);
if (!obj.GetType().IsValueType)
{
if (obj.GetType() == typeof(String))
{
Console.WriteLine(obj.ToString());
}
else if (obj.GetType() == typeof(List<T>))
{
//run a loop and print the list
}
else if (obj.GetType().IsArray) // this means its Array
{
//run a loop to print the array
}
}
else
{
//its primitive so we will convert to string
Console.WriteLine(obj.ToString());
}
I think you want something like this:
public class Program
{
public static void PrintProperties<T>(T t)
{
var properties = t.GetType().GetProperties();
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(t, null);
if (property.PropertyType.IsGenericType && property.PropertyType == typeof(IEnumerable<>))
{
var formatList = typeof(Program).GetMethod("FormatList", new[] { value.GetType() });
// value.GetType().GetGenericArguments().First() will get you the underlying type of the list,
// i.e., the TItemType where the property you are currently
// handling is of type IEnumerable<TItemType>
formatList.MakeGenericMethod(value.GetType().GetGenericArguments().First());
value = formatList.Invoke(null, new object[] { value });
Console.Out.WriteLine(name + ": " + value);
}
else
{
Console.Out.WriteLine(name + ": " + value);
}
}
}
public static string FormatList<TPlaceholder>(IEnumerable<TPlaceholder> l)
{
return string.Join(", ", l);
}
}
The code is untested but basically, you want to tackle enumerable types differently as compared to scalar values, so once you hit something of the type IEnumerable<TItemType>, you make a call to the FormatList<TPlaceholder> method.
Now, bear in mind that your original T and TItemType are not necessarily the same. When you invoke FormatList using reflection, you want to bind the TPlaceholder to TItemType. Once you have done that, you just invoke the formatting method and pass it the actual instance of the list, which returns you a string. That string you can then just output.
Hope that helps.
Borrowing heavily on the above examples, here is my full solution. This has been tested and handles IEnumerable's being passed in by printing out the properties of each element. It's not recursive but that's easy enough to add.
Be aware that many of the above examples will crash with indexed properties (lists for example).
Parameter count mismatch in property.GetValue().
It's avoided here by filtering properties which have indexed properties using this bit of LINQ.
Where(x=>!x.GetIndexParameters().Any())
Full example in the form of an extension method below.
/// <summary>
/// Returns string representation of object property states i.e. Name: Jim, Age: 43
/// </summary>
public static string GetPropertyStateList(this object obj)
{
if (obj == null) return "Object null";
var sb = new StringBuilder();
var enumerable = obj as IEnumerable;
if (enumerable != null)
{
foreach (var listitem in enumerable)
{
sb.AppendLine();
sb.Append(ReadProperties(listitem));
}
}
else
{
sb.Append(ReadProperties(obj));
}
return sb.ToString();
}
private static string ReadProperties(object obj)
{
var sb = new StringBuilder();
var propertyInfos = obj.GetType().GetProperties().OrderBy(x => x.Name).Where(x=>!x.GetIndexParameters().Any());
foreach (var prop in propertyInfos)
{
var value = prop.GetValue(obj, null) ?? "(null)";
sb.AppendLine(prop.Name + ": " + value);
}
return sb.ToString();
}

Is there a quick way to convert an entity to .csv file?

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

Categories

Resources