在.net中说到对excel的读取大家首先想到的可能是NPOI、LinqToExcel,最近在项目中用到Epplus,觉得也不错。
Epplus可以在NuGet中安装
我想达到的效果是导入Excel,直接转成想要的泛型集合。
我有个Person的类
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime? Birthday { get; set; }
}
希望最后的使用可能是这样的
var list = worksheet.ConvertSheetToObjects<Person>();
想一想...该怎么做.......
1、自定义Attribute
告诉Person类型 Name对应-->"姓名",Age对应-->"年龄",Birthday对应-->"生日"。
所以我们需要自己先定义这个标签类:
/// <summary>
/// 自定义excel头部标签
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class ExcelColumn : Attribute
{
/// <summary>
/// 标签名称
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name"></param>
public ExcelColumn(string name)
{
ColumnName = name;
}
}
2、给Person类的字段设置标签
public class Person
{
[ExcelColumn("姓名")]
public string Name { get; set; }
[ExcelColumn("年龄")]
public int Age { get; set; }
[ExcelColumn("生日")]
public DateTime? Birthday { get; set; }
}
3、对Epplus进行扩展,写个扩展类吧
public static class EppLusExtensions
{
/// <summary>
/// 获取标签对应excel的Index
/// </summary>
/// <param name="ws"></param>
/// <param name="columnName"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static int GetColumnByName(this ExcelWorksheet ws, string columnName)
{
if (ws == null) throw new ArgumentNullException(nameof(ws));
return ws.Cells["1:1"].First(c => c.Value.ToString() == columnName).Start.Column;
}
/// <summary>
/// 扩展方法
/// </summary>
/// <param name="worksheet"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> ConvertSheetToObjects<T>(this ExcelWorksheet worksheet) where T : new()
{
Func<CustomAttributeData, bool> columnOnly = y => y.AttributeType == typeof(ExcelColumn);
var columns = typeof(T)
.GetProperties()
.Where(x => x.CustomAttributes.Any(columnOnly))
.Select(p => new
{
Property = p,
Column = p.GetCustomAttributes<ExcelColumn>().First().ColumnName
}).ToList();
var rows = worksheet.Cells
.Select(cell => cell.Start.Row)
.Distinct()
.OrderBy(x => x);
var collection = rows.Skip(1)
.Select(row =>
{
var tnew = new T();
columns.ForEach(col =>
{
var val = worksheet.Cells[row, GetColumnByName(worksheet, col.Column)];
if (val.Value == null)
{
col.Property.SetValue(tnew, null);
return;
}
// 如果Person类的对应字段是int的,该怎么怎么做……
if (col.Property.PropertyType == typeof(int))
{
col.Property.SetValue(tnew, val.GetValue<int>());
return;
}
// 如果Person类的对应字段是double的,该怎么怎么做……
if (col.Property.PropertyType == typeof(double))
{
col.Property.SetValue(tnew, val.GetValue<double>());
return;
}
// 如果Person类的对应字段是DateTime?的,该怎么怎么做……
if (col.Property.PropertyType == typeof(DateTime?))
{
col.Property.SetValue(tnew, val.GetValue<DateTime?>());
return;
}
// 如果Person类的对应字段是DateTime的,该怎么怎么做……
if (col.Property.PropertyType == typeof(DateTime))
{
col.Property.SetValue(tnew, val.GetValue<DateTime>());
return;
}
// 如果Person类的对应字段是bool的,该怎么怎么做……
if (col.Property.PropertyType == typeof(bool))
{
col.Property.SetValue(tnew, val.GetValue<bool>());
return;
}
col.Property.SetValue(tnew, val.GetValue<string>());
});
return tnew;
});
return collection;
}
}
4、使用
这里我模拟http上传了一个excel来进行操作。如果你是读取本地excel请自行脑补IO流的玩法或者加我微信63036596然后给我100块钱我教你
[HttpPost]
[Route("Import")]
public void Import()
{
var file = HttpContext.Current.Request.Files[0];
var package = new ExcelPackage(file.InputStream);
var workbook = package.Workbook;
var worksheet = workbook.Worksheets.First();
var result = worksheet.ConvertSheetToObjects<Person>().ToArray();
}
好了,可以愉快的玩耍了