EntityModelは次のように定義されます:Personnel has to link to a Country
LinqPadでこのコードを実行すると、生成されるSQLが最初のクエリで最適化されていない(すべてのフィールドが返される)ことがわかりますか?ここで何が欠けているか、間違っていますか?
クエリ1 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();
クエリ1 SQL
exec sp_executesql N'SELECT [t0].[Id], [t0].[Version], [t0].[Identifier], [t0].[Name], , [t0].[UpdatedBy] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = @p0',N'@p0 bigint',@p0=100000581
クエリ2 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds2 = Personnels.Where(p => p.Country == Country).Select(p => p.Id).ToArray();
personnelIds2.Dump();
クエリ2 SQL
exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = @p0',N'@p0 bigint',@p0=100000581
使用されるデータベースはSQL Express 2008です。また、LinqPadバージョンは4.43.06です
//var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = context.Personnels
.Where(p => p.Country.Id == 100000581)
.Select(p => p.Id)
.ToArray();
personnelIds.Dump();
これを試してください、それは良いはずです。
人事コレクションは、アクセス時に遅延読み込みを介して入力されるため、DBからすべてのフィールドが取得されます。何が起こっているのか...
// retrieves data and builds the single Country entity (if not null result)
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
// Country.Personnels accessor will lazy load and construct all Personnel entity objects related to this country entity object
// hence loading all of the fields
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
次のようなものが必要です:
// build base query projecting desired data
var personnelIdsQuery = dbContext.Countries
.Where( c => c.Id == 100000581 )
.Select( c => new
{
CountryId = c.Id,
PersonnelIds = c.Personnels.Select( p => p.Id )
}
// now do enumeration
// your example shows FirstOrDefault without OrderBy
// either use SingleOrDefault or specify an OrderBy prior to using FirstOrDefaul
var result = personnelIdsQuery.OrderBy( item => item.CountryId ).FirstOrDefault();
または:
var result = personnelIdsQuery.SingleOrDefault();
次に、nullでない場合はIDの配列を取得します
if( null != result )
{
var personnelIds = result.PersonnelIds;
}
従業員のPOCOでForeignKeyを明示的に定義していますか? EFでそれを省くことは一般的ですが、それを追加すると、このコードと結果のSQLの両方が大幅に簡素化されます:
public class Personnel
{
public Country Country { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
. . .
}
> update-database -f -verbose
var ids = db.Personnel.Where(p => p.CountryId == 100000581).Select(p => p.Id).ToArray();
また、人員を1つのクエリにグループ化することもできます
var groups =
(from p in Personnel
group p by p.CountryId into g
select new
{
CountryId = g.Key
PersonnelIds = p.Select(x => x.Id)
});
var personnelIds = groups.FirstOrDefault(g => g.Key == 100000581);