我想使用EF从数据库中获取记录并将值分配给DTO类。请考虑以下表格以获取Linq查询。
TableA,TableB,TableC
对于每个TableA记录,TableB中有多个记录。对于每个TableB记录,TableC中有多个记录。 现在我的DTO看起来像这样
public class TableA_DTO
{
public int tableA_rowid { get; set; }
//remaining tableA field definitions
public List<TableB_DTO> TableB_records { get; set; }
}
public class TableB_DTO
{
public int tableB_rowid { get; set; }
//remaining tableB field definitions
public List<TableC_DTO> 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中的每个字段设置所需的属性。我还要在TableA属性字段中使用名称TableB_records填充TableB中的所有匹配记录,并在TableB_DTO中填充TableB_DTO属性中TableC的所有匹配记录,名称为TableC_records
可以这样做吗?我需要改变什么?是linq查询还是我的映射方式
感谢您的时间......
答案 0 :(得分:6)
我会将您的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
}
}
};
答案 1 :(得分:4)
首先,我只需要询问您是否可以使用Entity Framework 4.1和POCO(DbContext)并避免使用DTO的altoghther吗?
假设答案是否定的,那一定是因为你没有撤回所有字段,或者你在某种程度上改变了数据的“形状”。
在这种情况下,您可以将LINQ查询更改为如下所示:
from t in table
where ...
select new DTOA()
{
TheDtoProperty = theTableProperty,
AndSoOn = AndSoOn
};
这样做的好处:如果打开SQL事件探查器,您应该看到只有您请求的列才能进入实际的SQL查询。如果先查询全部然后再拉取值,则所有列都将被拉下线。
答案 2 :(得分:0)
我会制作一个工厂方法,即:TableA_DTO CreateDTO(TableAItem item);
使用此功能,您只需将查询重写为:
IEnumerable<TableA_DTO> = TableA.AsEnumerable().Select(CreateDTO);
这将直接为您提供“DTO”对象的集合。
话虽如此,如果你使用的是Entity Framework,那么在这种情况下,最近版本中添加的EF Code First可能会更有用。
答案 3 :(得分:0)
<强>更新强>
正如其他人所指出的那样,在使用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日期一样,检索树状数据: - )。看一下以下示例: / p>
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 = c.B.A.Id,
tableA_Prop1 = c.B.A.Property1,
tableA_Prop2 = c.B.A.Property2,
tableA_PropN = c.B.A.PropertyN,
tableB_rowid = c.B.Id,
tableB_Property1 = c.B.Property1,
tableB_Property2 = c.B.Property2,
tableB_PropertyN = c.B.PropertyN,
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)来获得我们想要的结构中的数据。
请注意,这也允许您进行分页和排序。这会有点棘手,但肯定不是不可能。