web-dev-qa-db-fra.com

Sélectionnez une seule colonne dans LINQ

EntityModel est défini comme suit: Personnel a un lien vers un pays

Lors de l'exécution de ce code dans LinqPad, je constate que le code SQL généré n'est pas optimisé (tous les champs sont renvoyés) dans la première requête? Qu'est-ce qui me manque ici ou ce qui ne va pas?

Requête 1 LINQ

var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();

Requête 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



Requête 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();

Requête 2 SQL

exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = @p0',N'@p0 bigint',@p0=100000581


La base de données utilisée est SQL Express 2008. La version de LinqPad est la version 4.43.06.

18
Stef Heyenrath
//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();

Essayez ceci, ça devrait être mieux. 

26
AD.Net

La collection Personnels sera remplie via un chargement différé lors de l'accès, ce qui permettra d'extraire tous les champs de la base de données. Voici ce qui se passe ...

// 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();

Vous voulez quelque chose de plus comme ça:

// 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();

OU:

var result = personnelIdsQuery.SingleOrDefault();

Ensuite, obtenez le tableau d'ID sinon NULL

if( null != result )
{
    var personnelIds = result.PersonnelIds;
}
3
Moho

Essayez peut également essayer de regrouper le personnel dans une seule requête

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);
1
p.s.w.g

La ForeignKey est-elle explicitement définie dans votre POCO for Personnel? Il est courant de le laisser dans EF, mais son ajout simplifierait considérablement ce code et le code SQL résultant:

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();
0
Chris Moschini