2011-11-21 6 views
5

Sto cercando di fare questo, ma dice che non posso usare FirstOrDefault,DbSet.FirstOrDefault()?

public static int GetId(this Context db, Type type, string name) 
{ 
    return db.Set(type).FirstOrDefault(x => x.Name == name).Id; 
} 

L'errore è 'System.Data.Entity.DbSet' non contiene una definizione per 'FirstOrDefault' e nessun metodo di estensione 'FirstOrDefault' accettare un primo argomento di tipo 'System.Data.Entity.DbSet' stato trovato (che le manca un un riferimento all'assembly direttiva using o?)

allora ho provato questo metodo, ma Cast che ha dato un errore Impossibile creare un DbSe t da un DbSet non generico per oggetti di tipo 'WindowStyle' (indipendente tra WindowStyle eredita da DomainEntity sotto),

var set = db.Set(type).Cast<DomainEntity>(); 
return set.FirstOrDefault(x => x.Name == name).Id; 

Ecco classe,

public class DomainEntity 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
} 
+9

Ti hanno 'utilizzando System.Linq' nella parte superiore del file? DbSet eredita IEnumerable ma le funzioni LINQ sono tutti metodi di estensione e non saranno disponibili senza l'istruzione using. –

+0

Il tuo secondo errore ('Cast') è un errore in fase di compilazione o in fase di esecuzione? Questo farà una grande differenza, ed è difficile da dedurre dalla tua domanda. –

+0

@ Chris, sì, l'ho fatto. Buona domanda. – Benjamin

risposta

8

Questa risposta forse non ti aiuta a seconda di come "dinamica" la situazione è dove si chiama questo metodo, in fondo se si conosce il tipo al momento della compilazione oppure no.Se si sa che si può scrivere un metodo generico invece:

public static class MyExtensions 
{ 
    public static int? GetId<TEntity>(this Context db, string name) 
     where TEntity : DomainEntity 
    { 
     return db.Set<TEntity>() 
      .Where(x => x.Name == name) 
      .Select(x => (int?)x.Id) 
      .FirstOrDefault(); 
    } 
} 

ho cambiato in una proiezione perché non c'è bisogno di caricare l'entità completa se desideri solo l'ID. Puoi lasciare che il database faccia il lavoro per selezionare la proprietà per migliorare un po 'le prestazioni. Inoltre restituisco un int nullo nel caso in cui non ci sia una corrispondenza per il nome nel database.

È possibile chiamare questo nel codice in questo modo:

int? id = db.GetId<WindowStyle>("abc"); 

Come si può vedere per questa soluzione è necessario specificare il tipo di WindowStyle in fase di compilazione.

Ciò presuppone che DomainEntity non faccia parte del modello (non esiste DbSet<DomainEntity>), ma solo una classe base delle entità. Altrimenti la soluzione di @Paul Keister sarebbe più semplice.

Modifica

In alternativa si può anche provare la seguente:

public static class MyExtensions 
{ 
    public static int? GetId(this Context db, Type entityType, string name) 
    { 
     return ((IQueryable<DomainEntity>)db.Set(entityType)) 
      .Where(x => x.Name == name) 
      .Select(x => (int?)x.Id) 
      .FirstOrDefault(); 
    } 
} 

e chiamarlo:

int? id = db.GetId("abc", someType); 

Sarà un'eccezione anche se in fase di esecuzione, se someType non eredita da DomainEntity. La versione generica controllerà questo al momento della compilazione. Quindi, se tu puoi preferisci la prima versione.

+0

Penso che questo mi aiuterà finalmente a imparare i farmaci generici! Grazie. – Benjamin

+0

Se potessi darti tutta la mia reputazione qui, vorrei - questo è quello che sto cercando da circa un anno e ho sempre fatto un approccio sbagliato. Thx, sei davvero il mio dio ora. – Zoka

0

Prova invertendo un bit do

 
set.Where(x => x.Name == name).Select(o=>o.Id).FirstOrDefault(); 

La vostra unità restituisce un'entità nulla e quindi tenta di ottenere l'ID da esso.

+0

Penso di aver provato 'Where' ma aveva lo stesso problema di' FirstOrDefault'. Grazie. – Benjamin

+0

Questo risolve un problema (uso improprio di 'FirstOrDefault'), ma non è correlato alla domanda. –

6

Il primo costrutto non funzionerà perché si sta lavorando con un DbSet non generico, quindi non è possibile applicare il metodo di estensione FirstOrDefault, che funziona solo con un generico. Sembra che tu lo capisca già, perché stai già cercando di ottenere il DbSet non generico. L'errore che si ottiene con il metodo Cast() è causato dal tentativo di eseguire il cast di un DbSet su un DbSet. Questo non può essere consentito, perché se fosse sarebbe possibile aggiungere membri non conformi al DbSet (oggetti di tipo diverso da WindowsStyle). Un altro modo per dire questo è che Covariance non è supportato per DbSet perché DbSets consente aggiunte.

Penso che dovrai trovare un altro modo per fare ciò che stai cercando di fare. Mescolare LINQ ed ereditarietà in questo modo è ovviamente problematico. Poiché hai definito un tipo di base e lavori solo con gli attributi disponibili sul tipo di base, perché non eseguire semplicemente una query sul tipo di base?

public static int GetId(this Context db, string name) 
    { 
     return db.DomainEntities.FirstOrDefault(x => x.Name == name).Id; 
    } 

probabilmente siete preoccupati per conflitti di nomi tra i vari tipi, ma probabilmente si può iniziare con questo e guardare le associazioni dei tipi di derivati ​​per verificare che si sta guardando il tipo corretto. Un modo per affrontarlo consiste nell'aggiungere un flag di tipo alla definizione DomainEntity.

+1

e cosa succede qui se il tuo oggetto è nullo e provi a fare riferimento a id:) –

+0

Paul, grazie. Ma non ho un 'DbSet ' su 'DbContext'. È per questo che sto avendo un problema? Stavo solo usando 'DomainEntity' per organizzare altre classi per ereditarietà. Penso che il vero problema sia che non capisco i farmaci generici. – Benjamin

+0

Ho provato a utilizzare "MakeGenericType" ma ha detto che "DbSet " non è un tipo generico. – Benjamin

2

Ecco un'idea che sembra funzionare.

public static int GetId(this Context db, Type type, string name) 
{ 
    var set = db.Set(type); 
    foreach (dynamic entry in set) 
     if (entry.Name == name) 
      return entry.Id; 
} 
+1

Dovresti essere in grado di sostituire 'dynamic' con' DomainEntity' qui, che ti darà quasi la stessa risposta. –

+3

Il caricamento di una tabella completa in memoria (forse 1 milione di righe) per ottenere un solo ID sembra molto poco efficiente. L'inizio del ciclo eseguirà la query 'set' senza alcun filtro. Stai lontano da questa soluzione. – Slauma

+0

@Slauma, inoltre, restituisce la corrispondenza dopo aver ripetuto l'intera tabella :) post modificato. – Shimmy

3

Ecco il problema. La classe DbSethas its own implementation of Cast<T>() che consente SOLO tipi di database (come Cast<WindowStyle>()), quindi questo metodo non consente Cast<DomainEntity>() e genera l'eccezione.

Invece, si desidera utilizzare il metodo di estensione IQueryable.Cast<T>(), che semplicemente trasmetterà i dati al tipo di base. Ecco un esempio:

var set = ((IQueryable)db.Set(type)).Cast<DomainEntity>(); 
return set.First(x => x.Name == name).Id; 
23

Forse vi manca

using System.Linq; 
+0

Questo mi ha risolto come un incantesimo! ;) – Sammy

+0

Ho dimenticato di assicurarmi che System.Linq fosse in uso! È quasi sempre presente per impostazione predefinita e non ho mai pensato di verificarlo. –