DAL/BLL架构中如何返回关联实体属性?求正确实现方案
解决DALServiceRequest嵌套DALRequest属性无法映射的问题
我来帮你分析下这个问题,其实核心原因是你用的Common.ToList(dt)通用转换方法不支持嵌套对象的属性映射——DataTable返回的是扁平的列结构,而你的DALServiceRequest里包含了一个嵌套的DALRequest对象,默认的反射转换只会处理当前类的直接属性,不会自动识别并填充嵌套对象。下面给你几个可行的解决方案:
方案一:修改通用转换方法,支持嵌套属性映射
如果想继续用Common.ToList,可以扩展它的逻辑,让它能识别嵌套的复杂类型,并根据列名前缀(比如Request_RequestId)来映射到嵌套对象的属性。
首先假设你的实体结构是这样的:
public class DALServiceRequest { // ServiceRequest自身属性 public int Id { get; set; } public string ServiceName { get; set; } // 嵌套的Request属性 public DALRequest Request { get; set; } } public class DALRequest { public int RequestId { get; set; } public string RequestTitle { get; set; } // 其他Request属性 }
修改后的Common.ToList方法示例:
public static class Common { public static List<T> ToList<T>(DataTable dt) where T : new() { var resultList = new List<T>(); foreach (DataRow row in dt.Rows) { var entity = MapRowToNestedObject<T>(row); resultList.Add(entity); } return resultList; } private static T MapRowToNestedObject<T>(DataRow row) where T : new() { var entity = new T(); foreach (var prop in typeof(T).GetProperties()) { // 处理简单类型(值类型、字符串) if (prop.PropertyType.IsValueType || prop.PropertyType == typeof(string)) { if (row.Table.Columns.Contains(prop.Name) && row[prop.Name] != DBNull.Value) { prop.SetValue(entity, Convert.ChangeType(row[prop.Name], prop.PropertyType)); } } // 处理嵌套的复杂类型 else { var nestedEntity = Activator.CreateInstance(prop.PropertyType); // 按「嵌套对象名_属性名」的规则匹配列名,比如Request_RequestId foreach (var nestedProp in prop.PropertyType.GetProperties()) { var columnName = $"{prop.Name}_{nestedProp.Name}"; if (row.Table.Columns.Contains(columnName) && row[columnName] != DBNull.Value) { nestedProp.SetValue(nestedEntity, Convert.ChangeType(row[columnName], nestedProp.PropertyType)); } } prop.SetValue(entity, nestedEntity); } } return entity; } }
注意:这个方法要求你的存储过程返回的列名必须是嵌套对象名_属性名的格式(比如Request_RequestId),这样才能正确映射到DALRequest.RequestId。
方案二:手动映射,绕过通用方法
如果不想修改通用工具类,直接手动遍历DataRow并赋值是最直接的方式,适合字段不多的场景:
public class BLLServiceRequests { public List<DALServiceRequest> GetServiceRequests() { // 调用存储过程获取DataTable DataTable dt = YourStoredProcedureCall(); var serviceRequests = new List<DALServiceRequest>(); foreach (DataRow row in dt.Rows) { var serviceReq = new DALServiceRequest { // 赋值ServiceRequest自身属性 Id = Convert.ToInt32(row["Id"]), ServiceName = row["ServiceName"].ToString(), // 初始化并赋值嵌套的DALRequest Request = new DALRequest { RequestId = Convert.ToInt32(row["RequestId"]), RequestTitle = row["RequestTitle"].ToString() // 其他Request属性依次赋值 } }; serviceRequests.Add(serviceReq); } return serviceRequests; } }
方案三:用ORM工具简化映射(推荐)
如果可以引入第三方库,Dapper是非常适合这种场景的轻量ORM,它原生支持嵌套对象的自动映射,不需要写复杂的反射逻辑。
先确保你已经通过NuGet安装了Dapper,然后可以这样写:
using Dapper; using System.Data.SqlClient; public class BLLServiceRequests { private readonly string _connectionString = "你的数据库连接字符串"; public List<DALServiceRequest> GetServiceRequests() { using (var conn = new SqlConnection(_connectionString)) { conn.Open(); // 调用存储过程,Dapper自动映射嵌套对象 var result = conn.Query<DALServiceRequest, DALRequest, DALServiceRequest>( sql: "你的存储过程名称", map: (serviceReq, req) => { serviceReq.Request = req; return serviceReq; }, splitOn: "RequestId", // 指定从哪一列开始属于DALRequest的属性 commandType: System.Data.CommandType.StoredProcedure ).ToList(); return result; } } }
这里的splitOn参数很关键,它告诉Dapper从RequestId列开始,后面的列都映射到DALRequest对象的属性上,完美适配你Join查询的结果。
内容的提问来源于stack exchange,提问作者Harambe Attack Helicopter
相关产品推荐
相关产品推荐

