ASP.NET Core中如何通过LINQ to Entities获取动态列数据?
问题根源
你当前的代码中,select ($"new {string.Join(", ", columns)} ")只是将用户传入的列名拼接成字符串返回,并没有真正从Children和Centers实体中提取对应列的实际数据,因此返回的只是列名字符串列表,而非报表需要的业务数据。
解决方案
以下提供两种可行的修改方案,你可以根据项目需求选择:
方案1:使用反射构建动态对象(无需额外依赖)
这种方式通过手动反射实体属性,将指定列的数据封装到动态对象中,适合小型场景或无法引入第三方库的情况。
修改后的代码
public async Task<Collection_Response<dynamic>> GetReportDataAsync(int Report_Type, List<string> columns, DateTime Start_Date, DateTime End_Date, string Lang) { Collection_Response<dynamic> result = new Collection_Response<dynamic>() { Is_Success = false, Messages = new List<Message_Item>() }; try { // 先执行基础关联和过滤查询,保留关联的两个实体 var baseQuery = from p in db.Children join e in db.Centers on p.Center_ID equals e.Center_ID where p.Joining_Date >= Start_Date && p.Joining_Date <= End_Date select new { Child = p, Center = e }; // 异步获取查询结果 var dataList = await baseQuery.ToListAsync(); // 转换为仅包含指定列的动态对象列表 var reportData = new List<dynamic>(); foreach (var item in dataList) { var expando = (ExpandoObject)new ExpandoObject(); var expandoDict = expando as IDictionary<string, object>; foreach (var column in columns) { // 优先从Child实体取属性 var childProp = item.Child.GetType().GetProperty(column); if (childProp != null) { expandoDict[column] = childProp.GetValue(item.Child); continue; } // 从Center实体取属性 var centerProp = item.Center.GetType().GetProperty(column); if (centerProp != null) { expandoDict[column] = centerProp.GetValue(item.Center); continue; } // 列名不存在时的处理(可根据需求调整,比如返回null或添加错误提示) expandoDict[column] = null; } reportData.Add(expando); } result.Data = reportData; result.Is_Success = true; } catch (Exception ex) { result.Messages.Add(new Message_Item() { Message = err.Handle_Exception(ex) }); } return result; }
方案2:使用System.Linq.Dynamic.Core实现动态查询(推荐)
通过第三方库直接构建动态LINQ查询,在数据库层面就只返回需要的列,性能更优,适合数据量较大的场景。
步骤1:安装依赖包
在NuGet包管理器中安装System.Linq.Dynamic.Core:
Install-Package System.Linq.Dynamic.Core
修改后的代码
using System.Linq.Dynamic.Core; public async Task<Collection_Response<dynamic>> GetReportDataAsync(int Report_Type, List<string> columns, DateTime Start_Date, DateTime End_Date, string Lang) { Collection_Response<dynamic> result = new Collection_Response<dynamic>() { Is_Success = false, Messages = new List<Message_Item>() }; try { // 【关键安全步骤】验证列名是否在白名单内,防止恶意列名导致的信息泄露 var allowedColumns = new List<string> { "p.ChildID", "p.Name", "p.Joining_Date", "e.Center_ID", "e.CenterName" }; // 替换为实际允许访问的列名(需包含实体别名,比如p代表Children,e代表Centers) foreach (var column in columns) { if (!allowedColumns.Contains(column)) { result.Messages.Add(new Message_Item() { Message = $"列名 {column} 不允许访问" }); return result; } } // 构建动态select语句 var selectClause = string.Join(", ", columns); // 执行动态查询 var reportData = await db.Children .Join(db.Centers, p => p.Center_ID, e => e.Center_ID, (p, e) => new { p, e }) .Where("p.Joining_Date >= @0 && p.Joining_Date <= @1", Start_Date, End_Date) .Select(selectClause) .ToDynamicListAsync(); result.Data = reportData; result.Is_Success = true; } catch (Exception ex) { result.Messages.Add(new Message_Item() { Message = err.Handle_Exception(ex) }); } return result; }
重要注意事项
- 安全性:无论使用哪种方案,必须对用户传入的列名进行白名单验证,禁止访问敏感字段(比如密码、内部ID等),防止信息泄露或恶意攻击。
- 返回类型调整:将原返回类型
Collection_Response<string>改为Collection_Response<dynamic>,因为需要返回包含多列数据的对象,而非单一字符串。
内容的提问来源于stack exchange,提问作者Arwa
相关产品推荐
相关产品推荐

