\$\begingroup\$
\$\endgroup\$
So, I'm building export/import CSV helper. I have some performance issues in the code below. it takes me to parse CSV of 25,000 rows at 7 seconds. If someone can help, it will be awesome!
public System.IO.Stream ParseContent<T>(IEnumerable<T> entities) where T : class
{
if (entities == null)
throw new ArgumentException(nameof(entities), "List accepted is empty.");
Type type = entities.First().GetType();
PropertyInfo[] properties = type.GetProperties();
string headers = GenerateTemplate(properties);
//No headers accepted - cannot export the content
if (string.IsNullOrEmpty(headers))
return null;
string contentToExport = $"{headers}{NewLineDelimiter}";
foreach (T entity in entities)
{
if (entity == null)
continue;
string template = this.ExportLine(entity, properties);
contentToExport += $"{template}{NewLineDelimiter}";
}
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(contentToExport);
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(bytes);
return memoryStream;
}
private string ExportLine<T>(T entity, PropertyInfo[] properties) where T : class
{
if (entity == null || properties == null)
return string.Empty;
string template = "";
foreach (PropertyInfo property in properties)
{
string value = null;
if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
Type underlyingType = property.PropertyType.GetGenericArguments()[0];
if (underlyingType.IsValueType || underlyingType == typeof(string))
{
System.Collections.IEnumerable list = (System.Collections.IEnumerable)property.GetValue(entity);
value = string.Join(EnumerableValueDelimiter, list.Cast<string>());
}
}
else if (property.PropertyType.IsClass && (!property.PropertyType.IsPrimitive && !property.PropertyType.IsEnum) && property.PropertyType != typeof(string))
{
//Object type. need to be serialized
object propertyValue = property.GetValue(entity);
if (propertyValue != null)
value = JsonConvert.SerializeObject(propertyValue);
else
value = "null";
}
else
{
value = property.GetValue(entity)?.ToString();
}
if (string.IsNullOrEmpty(value))
value = "";
template += $"{value}{LineValuesDelimiter}";
}
//Removing the last delimiter at the row.
if (template.Length > 0)
template = template.Remove(template.Length - 1, 1);
return template;
}
t3chb0t
44.6k9 gold badges84 silver badges190 bronze badges
1 Answer 1
\$\begingroup\$
\$\endgroup\$
2
ParseContent()
Type type = entities.First().GetType();
can throw an exception ifentities
doesn't contain any items. I may be wrong but you could use theT
as well likeType type = typeof(T);
.- If
entities
isnull
anArgumentNullException
should be thrown instead of anArgumentException
. - The
foreach
could be simplified and you should use aStringBuilder
instead of concating strings in a loop. Thats because strings are immutable and for eachcontentToExport += $"{template}{NewLineDelimiter}";
you create a new string object. - If the right hand side of an assignment makes the type clear one should use
var
instead of the concrete type. - Omitting braces
{}
althought they might be optional can lead to hidden and therefor hard to find bugs. I would like to encourage you to always use them. - Having a variable
memoryStream
doesn't buy you anything. Just return the new memorystream.
Applying these points will lead to
public System.IO.Stream ParseContent<T>(IEnumerable<T> entities) where T : class
{
if (entities == null)
{
throw new ArgumentNullException(nameof(entities), "List accepted is empty.");
}
if (!entities.Any())
{
//assuming thats the desired behaviour
return null;
}
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
string headers = GenerateTemplate(properties);
//No headers accepted - cannot export the content
if (string.IsNullOrEmpty(headers))
{
return null;
}
StringBuilder contentToExport = new StringBuilder( $"{headers}{NewLineDelimiter}");
foreach (T entity in entities.Where(e=>e!=null))
{
string template = this.ExportLine(entity, properties);
contentToExport.Append($"{template}{NewLineDelimiter}");
}
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(contentToExport.ToString());
return new System.IO.MemoryStream(bytes);
}
answered Dec 13, 2018 at 12:01
-
\$\begingroup\$ I knew strings are immutables, didn't think that it would cause that performance. Thank you very much \$\endgroup\$o.Nassie– o.Nassie2018年12月13日 12:56:05 +00:00Commented Dec 13, 2018 at 12:56
-
1\$\begingroup\$ Some small additions: It's generally not advised to enumerate an
IEnumerable
more than once, it may not support multiple enumerations or it could be backed by an expensive database query.typeof(T)
andentities.First().GetType()
can be different types and not every element inentities
is guaranteed to be the same type asentities.First().GetType()
. \$\endgroup\$Johnbot– Johnbot2018年12月13日 13:13:55 +00:00Commented Dec 13, 2018 at 13:13
lang-cs