2015-12-01 7 views
7

prega, supponiamo che l'architettura:EF condizionale Calcola per tipo di entità

public class Mammal 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Dog : Mammal 
{ 
    public int TailId { get; set; } 
    public Tail Tail { get; set; } 
} 

public class Bat : Mammal 
{ 
    public int WingId { get; set; } 
    public Wing Wing { get; set; } 
} 

public class Buffalo : Mammal 
{ 
    public virtual ICollection<Horn> Horns { get; set; } 
} 

public class Tail 
{ 
    public int Id { get; set; } 
    ... 
} 

public class Wing 
{ 
    public int Id { get; set; } 
    ... 
} 

public class Horn 
{ 
    public int Id { get; set; } 
    ... 
} 

Ora, mio ​​contesto:

public class MyContext : DbContext 
{ 
    public DbSet<Mammal> Mammals { get; set; } 
} 

Quindi, voglio fare solo una query SQL, e comprendono (e il carico) tutte le entità nidificate, qualcosa di simile:

var query = myContext.Mammals 
    .IncludeIfTypeIs<Dog>(d => d.Tail) 
    .IncludeIfTypeIs<Bat>(b => b.Wing) 
    .IncludeIfTypeIs<Buffalo>(b => b.Horns) 
    ... 
    ... 
; 

so che posso farlo separatamente, ma non voglio perché ho molte entità, e ho bisogno di minimizzare le richieste del database.

Non voglio utilizzare il caricamento lazy perché questo farà anche molte richieste di database.

Come raggiungerlo?

+0

credo che questo non è possibile in quanto includono lavori sulla raccolta e sulla base di tale prepara la query ed esegue sql join ma per il tuo caso sembra che stia lavorando su una singola istanza di 'Mammal' che credo possa vanificare totalmente lo scopo del linq in sql. –

+0

Grazie per il tuo commento, ma non credo che riusciremo a sconfiggere lo scopo di linq in sql. Senza questo sono obbligato a creare una query per ogni tipo di mamal e chiamare tutto ciò che è separato, questo sarà in ritardo e complicherà la mia applicazione. Nella mia applicazione, posso avere, ad esempio, diversi tipi di Horns, con proprietà diverse, e voglio anche caricare queste proprietà conditionnally ... –

+0

Non avresti bisogno di iterare su tutti i mamals e controllare i loro tipi e includere solo se è di quel tipo ....? –

risposta

0

Puoi provare qualcosa di simile:

public static class Extensions 
{ 
    public static IQueryable<Mammal> IncludeExtraEntities<Mammal,T>(this IQueryable<Mammal> query, T derivedType) where T :Mammal 
    { 
     if (derivedType is Dog) 
      return query.Include("Tail"); 
     if (derivedType is Bat) 
      return query.Include("Wing"); 
     return query; 
    } 
} 

Poi nella chiamata db:

var query = myContext.Mammals.IncludeExtraEntities(typeof(Dog)); 

Forse questo funzionerà.

+0

No, questo non funziona, perché ho bisogno di caricare entità nidificate per tutti i tipi, nella stessa query. –

+1

Ma come si ottiene il tipo? Intendi il tipo di qualsiasi "Mammifero" che verrebbe recuperato dalla tua query? Quindi se uno dei mammiferi è un cane, carica le code per quell'unico risultato, se un altro è un pipistrello, carica le ali per quel risultato? –

+2

Afaik non è possibile caricare entità di tipi derivati ​​diversi –

0

È possibile creare un metodo per includere un elenco di espressioni. (o forse un metodo di estensione).

public static IQueryable<Mammal> GetMammals(params Expression<Func<T, Object>>[] includeExps) 
{ 
    var query = context.Mammals.AsQueryable(); 
    if (includeExps != null) 
     query = includeExps.Aggregate(query, (current, exp) => current.Include(exp)); 

    return query; 

} 

E poi, nel codice:

//Bat and Dog will be included here 
var mammals = GetMammals(i => i.Bat, i => i.Dog); 

Speranza che aiuta!

+0

Questo non funziona. Suppongo che il tuo tipo T sia Mammifero (non lo specifichi). Quindi, se chiamo GetMammals, questo attende una proprietà di un Mammifero (Cane e pipistrello è un tipo di mammifero, non una proprietà). Se provo a fare m => m.Wing, per esempio, non verrà compilato perché Mammal non ha proprietà Wing (Bat ha questa proprietà). Quindi, l'inclusione condizionale deve convertire il tipo di le prima per includerlo .... –

+0

Oh, ho frainteso. Quindi, credo che non sia possibile, come @Alexander Derck ha detto –

0

La domanda è, come si caricheranno le proprietà dei tipi derivati, utilizzando solo un DbSet della classe base e senza un caricamento lazy? Temo che questo non sia possibile.

È possibile comunque fare questo:

public class MyContext : DbContext 
{ 
    public DbSet<Mammal> Mammals { get; set; } 
    public DbSet<Dog> Dogs { get; set; } 
    public DbSet<Bat> Bats { get; set; } 
} 

si potrebbe anche semplicemente implementare un metodo, specifici per tipo per ciascuno dei vostri tipi

public static class Extensions 
{ 
    public static IQueryable<Dog> IncludeExtraEntities<Dog>(this IQueryable<Dog> query) where Dog : Mammal 
    { 
      return query.Include("Tail"); 
    } 

    public static IQueryable<Bat> IncludeExtraEntities<Bat>(this IQueryable<Bat> query) where Bat: Mammal 
    { 
      return query.Include("Wing"); 
    } 
} 

Quindi, è possibile chiamare semplicemente:

myContext.Dogs.IncludeExtraEntities(); 

E verrà chiamato il metodo a seconda del tipo.

+0

Grazie, ma non è quello che voglio. Quello che mi serve è chiamare Mammals.IncludeForDog(). IncludeForBat(). IncludeForBuffalo() ... –

+0

Quindi avrò una lista di mamali di cani, pipistrelli e bufali, e per ognuno le sue proprietà rispettive correttamente caricate. –

0

Ti stai aspettando troppo da Include. L'espressione lambda inserita in Include è semplicemente un fornitore di stringhe che sembra troppo intelligente. Sotto il cofano la sua espressione membro viene sezionata per ottenere il nome della proprietà, questo è tutto. Il nome viene inserito nel metodo Include che accetta un parametro stringa. Quel metodo fa il vero lavoro.

L'espressione lambda deve puntare a una proprietà di navigazione del tipo nello IQueryable. Non si può fare ...

myContext.Mammals.Include(d => d.Tail) 

... perché Tail non è una proprietà del tipo di base.Si può solo fare ...

myContext.Mammals.OfType<Dog>().Include(d => d.Tail) 

Il meglio che si può ottenere è qualcosa di simile a

from m in context.Mammals 
let tail = (m as Dog).Tail 
let wing = (m as Bat).Wing 
let horns= (m as Buffalo).Horns 
select new { Mammal = m, tail, wing, horns } 

Dal momento che tutto si traduce in SQL non dovete preoccuparvi di eccezioni riferimento null.

0

ho dovuto affrontare problemi del genere e qui come ho risolto:

public IEnumerable<OrderLine> GetAllOrderLinesData() 
    { 
     var _StockOrderLines = appContext.OrderLines.OfType<StockOrderLine>() 
      .Include(p => p.Order).Include(p => p.Product); 

     var _AnnualOrderLines = appContext.OrderLines.OfType<AnnualOrderLine>() 
      .Include(p => p.Order).Include(p => p.Customer); 

     return _StockOrderLines.ToList<OrderLine>().Union<OrderLine>(_AnnualOrderLines.ToList<OrderLine>()); 
    } 

è possibile modificare le classi come la tua :)