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
{
///
/// Main Class that converts a Excel file to IEnumerable of the specified entity class
///
public class ExcelToEntity : IDisposable
{
///
/// The excel table has row header with the information
///
public bool HasHeaders { get; set; }
///
/// Excel Data Reader Interface to convert excel to data table
///
IExcelDataReader edr;
///
/// Manual contructor by File stream and excel version type
///
/// Can be a memory stream or file stream or byte array
/// Excel type of the file given (binary xls or open format xlsx)
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;
}
///
/// Automatic constructor based on html form post multipart input type file
///
/// posted file from asp html request or MVC post action
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;
}
///
/// Read the data table obtain from the converted excel file and transform to the entity class
///
/// Entity Class to transform to
/// Start row from the data table
/// Start column from the data table
/// sheet name where to read the data
///
public IEnumerable Read(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(r);
}
}
///
/// internal method to transform the excel file to a data table
///
/// Start row from the data table
/// Start column from the data table
/// sheet name where to read the data
///
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;
}
///
/// internal method to convert a data row to a data entity
///
/// Entity Class
/// Data row to grab the data from
///
private t GetEntity(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
{
property.SetValue(entity, v.GetType() == property.PropertyType ? v :
Convert.ChangeType(row[propName], property.PropertyType));
}
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;
}
///
/// implement the garbage colector and close the unmanaged excel to data table reader
///
public void Dispose()
{
if (!edr.IsClosed)
{
edr.Close();
}
}
}
///
/// Attribute to map a property to a column field in the excel file
///
public class ExcelColumnAttribute : Attribute
{
public ExcelColumnAttribute()
{
}
///
/// Attribute to map a property to a column field in the excel file
///
/// Name of the column in the excel file
public ExcelColumnAttribute(string Name)
{
this.Name = Name;
}
public string Name { get; set; }
}
///
/// Attribute to skip the mapping of a property in the selected entity class
///
public class ExcelNotMapAttribute : Attribute
{
public ExcelNotMapAttribute()
{
}
}
///
/// Possible excel file types supported by the reader
///
public enum ExcelType
{
///
/// Microsoft Office Excel 2007 and later Open XML Format
///
xlsx,
///
/// Microsoft Office Excel 2003 and earlier propiertary binary format
///
xls
}
}