YNICTE/Base/Lib/Excel2Entity.cs

240 lines
8.9 KiB
C#
Raw Normal View History

2020-10-12 14:39:23 +09:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Excel;
using System.IO;
namespace NP.Base.Lib
{
/// <summary>
/// Main Class that converts a Excel file to IEnumerable of the specified entity class
/// </summary>
public class ExcelToEntity : IDisposable
{
/// <summary>
/// The excel table has row header with the information
/// </summary>
public bool HasHeaders { get; set; }
/// <summary>
/// Excel Data Reader Interface to convert excel to data table
/// </summary>
IExcelDataReader edr;
/// <summary>
/// Manual contructor by File stream and excel version type
/// </summary>
/// <param name="s">Can be a memory stream or file stream or byte array</param>
/// <param name="et">Excel type of the file given (binary xls or open format xlsx)</param>
public ExcelToEntity(Stream s, ExcelType et)
{
HasHeaders = true;
switch (et)
{
case ExcelType.xlsx:
edr = ExcelReaderFactory.CreateOpenXmlReader(s);
break;
case ExcelType.xls:
edr = ExcelReaderFactory.CreateBinaryReader(s);
break;
default:
break;
}
edr.IsFirstRowAsColumnNames = false;
}
/// <summary>
/// Automatic constructor based on html form post multipart input type file
/// </summary>
/// <param name="File">posted file from asp html request or MVC post action</param>
public ExcelToEntity(System.Web.HttpPostedFileBase File)
{
if (File == null) throw new ArgumentNullException("HttpPostedFileBase File is null");
HasHeaders = true;
if (File.FileName.Trim().ToLower().EndsWith(".xlsx"))
edr = ExcelReaderFactory.CreateOpenXmlReader(File.InputStream);
else
edr = ExcelReaderFactory.CreateBinaryReader(File.InputStream);
edr.IsFirstRowAsColumnNames = false;
}
/// <summary>
/// Read the data table obtain from the converted excel file and transform to the entity class
/// </summary>
/// <typeparam name="T">Entity Class to transform to</typeparam>
/// <param name="StartRow">Start row from the data table</param>
/// <param name="StartColumn">Start column from the data table</param>
/// <param name="SheetName">sheet name where to read the data</param>
/// <returns></returns>
public IEnumerable<T> Read<T>(int StartRow = 1, int StartColumn = 1, string SheetName = null) where T : new()
{
var dt = getTable(StartRow, StartColumn, SheetName);
if (!HasHeaders) StartRow--;
for (int i = StartRow; i < dt.Rows.Count; i++)
{
var r = dt.Rows[i];
if (!(r.ItemArray.All(x => string.IsNullOrWhiteSpace(x.ToString()))))
yield return GetEntity<T>(r);
}
}
/// <summary>
/// internal method to transform the excel file to a data table
/// </summary>
/// <param name="startRow">Start row from the data table</param>
/// <param name="startCol">Start column from the data table</param>
/// <param name="sheetName">sheet name where to read the data</param>
/// <returns></returns>
private System.Data.DataTable getTable(int startRow, int startCol, string sheetName)
{
var ds = edr.AsDataSet();
System.Data.DataTable dt;
if (!string.IsNullOrEmpty(sheetName))
{
dt = ds.Tables[sheetName];
}
else
{
dt = ds.Tables[0];
}
if (HasHeaders)
{
var drHeader = dt.Rows[startRow - 1];
for (int i = startCol - 1; i < dt.Columns.Count; i++)
{
var name = drHeader[i];
if (name != null && name is string)
{
var cname = new String(((string)name).Where(c => char.IsLetterOrDigit(c)).ToArray());
if (char.IsDigit(cname.FirstOrDefault())) cname = "_" + cname;
if (!string.IsNullOrWhiteSpace(cname))
{
try
{
dt.Columns[i].ColumnName = cname;
}
catch (System.Data.DuplicateNameException)
{
dt.Columns[i].ColumnName = cname + "_" + i.ToString();
}
}
};
}
}
return dt;
}
/// <summary>
/// internal method to convert a data row to a data entity
/// </summary>
/// <typeparam name="t">Entity Class</typeparam>
/// <param name="row">Data row to grab the data from</param>
/// <returns></returns>
private t GetEntity<t>(System.Data.DataRow row) where t : new()
{
var entity = new t();
var properties = typeof(t).GetProperties();
foreach (var property in properties.Where(x => x.GetCustomAttributes(typeof(ExcelNotMapAttribute), true).SingleOrDefault() == null))
{
//Get the description attribute
var CustomAttribute = (ExcelColumnAttribute)property.GetCustomAttributes(typeof(ExcelColumnAttribute), true).SingleOrDefault();
string propName = CustomAttribute == null ? property.Name : CustomAttribute.Name;
object v = null;
try { v = row[propName]; }
catch (Exception) { continue; }
try
{
if (v.GetType() == property.PropertyType)
{
property.SetValue(entity, v);
}
else
{
if (property.PropertyType == typeof(DateTime?))
{
if (DateTime.TryParse(row[propName].ToString(), out DateTime dt))
{
property.SetValue(entity, dt);
}
else
{
property.SetValue(entity, null);
}
}
else
{
var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
property.SetValue(entity, Convert.ChangeType(row[propName], propertyType));
}
}
2020-10-12 14:39:23 +09:00
}
catch (Exception)
{
throw new InvalidCastException("Unable to cast value '" + v.ToString() +
"' to type '" + property.PropertyType.Name + "' for property: " + property.Name +
" row: " + row.ItemArray.Aggregate((a, b) => a.ToString() + "," + b.ToString()));
}
}
return entity;
}
/// <summary>
/// implement the garbage colector and close the unmanaged excel to data table reader
/// </summary>
public void Dispose()
{
if (!edr.IsClosed)
{
edr.Close();
}
}
}
/// <summary>
/// Attribute to map a property to a column field in the excel file
/// </summary>
public class ExcelColumnAttribute : Attribute
{
public ExcelColumnAttribute()
{
}
/// <summary>
/// Attribute to map a property to a column field in the excel file
/// </summary>
/// <param name="Name">Name of the column in the excel file</param>
public ExcelColumnAttribute(string Name)
{
this.Name = Name;
}
public string Name { get; set; }
}
/// <summary>
/// Attribute to skip the mapping of a property in the selected entity class
/// </summary>
public class ExcelNotMapAttribute : Attribute
{
public ExcelNotMapAttribute()
{
}
}
/// <summary>
/// Possible excel file types supported by the reader
/// </summary>
public enum ExcelType
{
/// <summary>
/// Microsoft Office Excel 2007 and later Open XML Format
/// </summary>
xlsx,
/// <summary>
/// Microsoft Office Excel 2003 and earlier propiertary binary format
/// </summary>
xls
}
}