Includo la proprietà di navigazione nella mia query con Include
, in modo che non venga caricata in seguito. Ma non funziona, quando creo un oggetto wrapper anonimo con la proiezione Select
.EF: proprietà di navigazione "Includi" quando si crea un oggetto wrapper con proiezione "Seleziona"
Lasciami mostrare l'esempio semplificato. L'entità:
public class UserEntity {
public string Name {get;set;}
public virtual ICollection<UserEntity> Friends { get; set; }
}
La query:
var entry = _dbCtx
.Users
.Include(x => x.Friends)
// Select here is simplified, but it shows the wrapping
.Select(user => new {
User = user
})
.First();
// Here we have additional lazy loaded DB call
var friends = entry.User.Friends.Select(x => x.Name).ToList();
E vedo anche da SQL generato, che la proprietà di navigazione non è incluso:
SELECT
[Limit1].[Name] AS [Name],
FROM (SELECT TOP (1)
[Extent1].[Name] AS [Name]
FROM [dbo].[Users] AS [Extent1]
) AS [Limit1]
E 'possibile a Include
la proprietà di navigazione Friends
in questo caso, in modo che User
otterrà i dati senza il caricamento lazy?
mi aspettavo anche questo lavoro:
var entry = _dbCtx
.Users
.Select(user => new {
User = user
})
.Include(x => x.User.Friends)
.First();
Ma ottenere un'eccezione:
InvalidOperationException: Il tipo di risultato della query non è né un EntityType né una CollectionType con un tipo di elemento un'entità . Un percorso di inclusione può essere specificato solo per una query con uno di questi tipi di risultati.
Ci sono alcune soluzioni sono venuto a, ma sono in qualche modo ingannevole:
Aggiungi proprietà aggiunta al nostro oggetto anonimo in
Select
:var entry = _dbCtx .Users .Select(user => new { User = user, UsersFriends = user.Friends }) .First(); // manually copy the navigation property entry.User.Friends = user.UsersFriends; // Now we don't have any addition queries var friends = entry.User.Friends.Select(x => x.Name).ToList();
Map anche l'utente a un oggetto anonimo a livello di DB, quindi mappare le proprietà su
UserEntity
in C#.var entry = _dbCtx .Users .Select(user => new { User = new { Name = user.Name, Friends = user.Friends } }) .Take(1) // Fetch the DB .ToList() .Select(x => new { User = new UserEntity { Name = x.Name, Friends = x.Friends } }) .First(); // Now we don't have any addition queries var friends = entry.User.Friends.Select(x => x.Name).ToList();
Così ora, c'è un LEFT OUTER JOIN
per Friends
, ma entrambe le soluzioni alternative non sono abbastanza buoni:
1) Ulteriori oggetti e una copia non è un modo pulito.
2) My UserEntity ha molte altre proprietà. Inoltre, ogni volta che aggiungiamo nuove proprietà, dovremmo modificare qui anche i selettori.
C'è un modo per ottenere la proprietà di navigazione incluso dal primo campione?
Grazie per la lettura e spero che qualcuno abbia un indizio per questo.
EDIT:
io dirigerò la l'entità e la query per mostrare un vero e proprio caso d'uso.
L'Entità
public class UserEntity {
public string Name {get;set;}
public int Score {get;set;}
public virtual ICollection<UserEntity> Friends { get; set; }
}
La query
var entry = _dbCtx
.Users
.Include(x => x.Friends)
.Select(user => new {
User = user,
Position = _dbCtx.Users.Count(y => y.Score > user.Score)
})
.First();
tuo campione funzionerà come si crea un oggetto wrapper sul lato 'C#' (dopo query), non con 'sql'. Ma ne avevo bisogno al livello 'db'. Motivo: eseguo subquery aggiuntive con 'Select'. Cosa interessante, vedi la mia prima soluzione, anche se usiamo 'user.Friends' nella query, ma dobbiamo copiare la proprietà su entità dopo la materializzazione, altrimenti avremmo ancora il caricamento lazy. – tenbits
Perché è necessario proiettare su un tipo anonimo nella query db? Finisci con lo stesso risultato per quanto posso dire. O stai proiettando i metodi _additional_ ('Where',' OrderBy', ecc.) Che vuoi proiettare su SQL? –
Vedere le mie modifiche post, eseguo una sottoquery per un utente e dovrebbe essere eseguita in DB, poiché scorre su ciascun utente. Quindi, a causa delle prestazioni, non posso farlo con 'C#'. – tenbits