[英]Mapping Linq Query results to a DTO class
我想使用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_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中填充名稱為TableB_records的所有匹配記錄,並在TableB_DTO中填充TableB_DTO屬性中TableC的所有匹配記錄,名稱為TableC_records
可以這樣做嗎? 我需要改變什么? 是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_DTO> = 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 = 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)來獲得我們想要的結構中的數據。
請注意,這也允許您進行分頁和排序。 這將有點棘手,但肯定不是不可能的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.