将Linq查询结果映射到DTO类

我想使用EF从数据库中获取记录并将值分配给DTO类。请考虑以下表格以获取Linq查询。

表A,表B,表C.

对于每个TableA记录,TableB中有多个记录。 对于每个TableB记录,TableC中有多个记录。 现在我的DTO看起来像这样

public class TableA_DTO { public int tableA_rowid { get; set; } //remaining tableA field definitions public List TableB_records { get; set; } } public class TableB_DTO { public int tableB_rowid { get; set; } //remaining tableB field definitions public List TableC_records { get; set; } } public class TableC_DTO { public int tableC_rowid { get; set; } //remaining tableC field definitions } 

我的linq查询看起来像这样

 var qry = from ent in TableA select ent; 

在我的映射类中,我遍历查询结果中的项目,如下所示:

  foreach (var dataitem in query) { TableA_DTO dto = new TableA_DTO(); dto.tableA_rowid = dataitem.ID; //remaining field definitions here } 

现在,这适用于TableA中的所有字段,它从数据库中提取一条记录,并在TableA_DTO中为TableA表中的每个字段设置所需的属性。 我还希望通过名称TableB_records在TableA属性字段中填充TableB中的所有匹配记录,并在TableB_DTO中通过名称TableC_records填充TableB_DTO属性中TableC的所有匹配记录

可以这样做吗? 我需要改变什么? 是linq查询还是我的映射方式

谢谢你的时间…

我会将您的DTO从List更改为IEnumerable而不是在LINQ查询中执行所有操作。

 var query = from ent in TableA select new TableA_DTO { TableAProperty = a.Property, TableB_records = from b in TableB where ent.Key == b.Key select new TableB_DTO { TableBProperty = b.Property, TableC_records = from c in TableC where b.Key == c.Key select new TableC_DTO { TableCProperty = c.Property } } }; 

首先,我只需要询问您是否可以使用Entity Framework 4.1和POCO(DbContext)并避免使用DTO的altoghther?

假设答案是否定的,那一定是因为你没有撤回所有字段,或者你正在以某种方式改变数据的“形状”。

在这种情况下,您可以将LINQ查询更改为如下所示:

 from t in table where ... select new DTOA() { TheDtoProperty = theTableProperty, AndSoOn = AndSoOn }; 

这样做的好处:如果打开SQL事件探查器,您应该看到只有您请求的列才能进入实际的SQL查询。 如果先查询全部然后再拉取值,则所有列都将被拉下线。

我会创建一个工厂方法,即: TableA_DTO CreateDTO(TableAItem item);

使用此方法,您可以将查询重写为:

 IEnumerable = TableA.AsEnumerable().Select(CreateDTO); 

这将直接为您提供“DTO”对象的集合。

话虽如此,如果您正在使用entity framework,在这种情况下,在最新版本中添加的EF Code First可能会更有用。

UPDATE

正如其他人所指出的那样,使用Entity Framework 4.0时不需要展平结果(如下所示),因为它可以将LINQ查询转换为有效的展平结果。 因此,仅在使用LINQ to SQL(或可能是其他LINQ提供程序)时才需要以下代码。 请注意,我仅使用EF over SQL Server而不是Oracle对此进行了测试,因为此行为可能是LINQ提供程序特定的,这意味着Oracle提供程序(仍处于测试阶段)或Oracle的商业Devart提供程序仍可以执行N + 1。


你要做的是获得一组结构像树的对象。 没有任何特别注意,您将触发对数据库的许多查询。 使用一级嵌套,您将触发N + 1个查询,但由于您的嵌套深度为两级,因此您将触发M x(N + 1)+ 1个查询,这几乎肯定会对性能造成很大影响(无论如何)你的数据集的大小是多少)。 您想要的是确保只有一个查询发送到数据库。 为了确保这一点,您必须创建一个中间查询来展平结果,就像在旧的SQL日期一样,检索树状数据:-)。 看一下下面的例子:

 var records = from record in db.TableC where ... // any filtering can be done here select record; // important to call ToArray. This ensures that the flatterned result // is pulled in one single SQL query. var results = ( from c in records select new { tableA_rowid = cBAId, tableA_Prop1 = cBAProperty1, tableA_Prop2 = cBAProperty2, tableA_PropN = cBAPropertyN, tableB_rowid = cBId, tableB_Property1 = cBProperty1, tableB_Property2 = cBProperty2, tableB_PropertyN = cBPropertyN, tableC_rowid = c.Id, tableC_Property1 = c.Property1, tableC_Property2 = c.Property2, tableC_PropertyN = c.PropertyN, }) .ToArray(); 

下一步是将内存中的数据结构(使用该匿名类型)转换为DTO对象的树结构:

 // translate the results to DTO tree structure TableA_DTO[] dtos = ( from aresult in results group aresult by aresult.tableA_rowid into group_a let a = group_a.First() select new TableA_DTO { tableA_rowid = a.tableA_rowid, tableA_Prop1 = a.tableA_Prop1, tableA_Prop2 = a.tableA_Prop2, TableB_records = ( from bresult in group_a group bresult by bresult.tableB_rowid into group_b let b = group_b.First() select new TableB_DTO { tableB_rowid = b.tableB_rowid, tableB_Prop1 = b.tableB_Prop1, tableB_Prop2 = b.tableB_Prop2, TableC_records = ( from c in group_b select new TableC_DTO { tableC_rowid = c.tableC_rowid, tableC_Prop1 = c.tableC_Prop1, tableC_Prop2 = c.tableC_Prop2, }).ToList(), }).ToList() }) .ToArray(); 

正如您所看到的,解决方案的第一部分实际上是执行此操作的“旧”方式,当我们仍然手动编写SQL查询时。 然而,好的是,一旦我们得到这种类型的内存数据,我们可以再次利用LINQ(到Objects)来获得我们想要的结构中的这些数据。

请注意,这也允许您进行分页和排序。 这将有点棘手,但肯定不是不可能的。