Asp.net MVC结合EF将List转DataTable填充Excel模板获取关联属性问题
问题解决方案
你遇到的问题由两个原因导致:
- EF Core
Include方法的使用错误 - 原有的
ToDataTable方法仅支持读取实体的直接属性,无法读取关联导航属性的嵌套字段
方案1:使用匿名类投影(最适合新手,无需修改现有转换方法)
这是成本最低的方案,你原来的ToDataTable方法不需要做任何改动,只需要调整查询逻辑,将需要的字段都投影到平级的匿名类中即可:
第一步:修正查询写法
你原来的.Include(x => x.Component.Select(c=>c.ReferenceNo))写法是错误的,Include加载单实体导航属性直接指定属性名即可,不需要嵌套Select:
public IActionResult Ex2(int id, int idc) { try { var data = DB.Activitydetails .Where(x => x.Activity.Moid == id && x.Activity.Mo.ClientId == idc) .Include(x => x.Component) // 正确加载关联的Component实体 .Select(a => new { // 保留原来需要的Activitydetail字段 a.ActivityDetailId, a.ActivityId, a.TimeType, a.Hours, a.ComponentId, a.OkQty, a.Comments, // 新增Component的字段,属性名和Excel占位符保持一致即可 ComponentName = a.Component.Name, ComponentReferenceNo = a.Component.ReferenceNo }) .ToList(); // 直接用投影后的匿名类List转DataTable var table11 = ToDataTable(data); table11.TableName = "table11"; // 后面的DataSet填充、导出逻辑完全不变 var ds = new DataSet(); ds.Tables.Add(table11); ds.Tables.Add("table2"); ds.Tables["table2"].Columns.Add("col1"); ds.Tables["table2"].Columns.Add("col2"); FillReport("new2.xlsx", "template_mo1.xlsx", ds); string Files = "new2.xlsx"; byte[] fileBytes = System.IO.File.ReadAllBytes(Files); return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, "new2.xlsx"); } catch (Exception ex) { // 建议此处添加错误日志,方便后续排查问题 throw; } }
第二步:调整Excel模板占位符
对应你投影的属性名,把Excel里的占位符改成%ComponentName%、%ComponentReferenceNo%即可正常匹配填充。
方案2:改造ToDataTable方法支持嵌套属性读取
如果你不想每次导出都手动写投影,可以改造转换方法,自动识别嵌套属性:
public DataTable ToDataTable<T>(List<T> items, List<string> nestedProps = null) { DataTable dataTable = new DataTable(typeof(T).Name); // 先添加实体的直接属性 PropertyInfo[] directProps = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo prop in directProps) { // 如果是类类型导航属性(排除字符串),不直接生成列 if(prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) continue; dataTable.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); } // 添加指定的嵌套属性列 if(nestedProps != null) { foreach(var propPath in nestedProps) { dataTable.Columns.Add(propPath.Replace(".", "")); } } // 填充行数据 foreach (T item in items) { var rowValues = new List<object>(); // 填充直接属性值 foreach (PropertyInfo prop in directProps) { if(prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) continue; rowValues.Add(prop.GetValue(item) ?? DBNull.Value); } // 填充嵌套属性值 if(nestedProps != null) { foreach(var propPath in nestedProps) { object value = item; foreach(var propName in propPath.Split('.')) { if(value == null) break; var prop = value.GetType().GetProperty(propName); value = prop.GetValue(value); } rowValues.Add(value ?? DBNull.Value); } } dataTable.Rows.Add(rowValues.ToArray()); } return dataTable; }
调用的时候只需要传入需要读取的嵌套属性路径即可:
var data = DB.Activitydetails .Where(x => x.Activity.Moid == id && x.Activity.Mo.ClientId == idc) .Include(x => x.Component) .ToList(); var table11 = ToDataTable(data, new List<string> { "Component.Name", "Component.ReferenceNo" });
Excel模板对应的占位符用%ComponentName%、%ComponentReferenceNo%即可。
内容的提问来源于stack exchange,提问作者Rodica A
相关产品推荐
相关产品推荐

