2015-03-23 15 views
10

Ho un metodo generico per interrogare oggetti di tipo TEntity in EF. Voglio aggiungere una condizione come clausola where se TEntity implementa un'interfaccia specifica. Il metodo che ho è:Fusioni Entità di interfaccia implementata in un metodo generico utilizzando LINQ per Entity Framework

public TEntity GetByUserID(Guid userID) 
{ 
    var query = this.DbSet; 
    if (typeof (TEntity).IsImplementationOf<IDeletableEntity>()) 
    { 
     query = query 
      .Where((x => !((IDeletableEntity)x).IsDeleted); 
    } 
    return query 
     .FirstOrDefault(x => x.UserID == userID); 
} 

IsImplementationOf <>() è il metodo che restituisce solo vero/falso, come suggerisce il nome.

Quando ho eseguito questo per l'Indirizzo entità che implementa IDeletableEntity, ottengo un errore:

Unable to cast the type 'Address' to type 'IDeletableEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.

Tutte le idee come posso andare in giro questa limitazione di LINQ?

risposta

-2

Credo che il problema potrebbe essere il cast diretto che si sta facendo nella sua dichiarazione, così come query potrebbe essere attuando i tipi di enumerazione e così IDeletable non è implementata come su un organismo.

LINQ-to-entities casting issue

proposto questa soluzione.

return query.ToList() 
         .Cast<IDeletable>() 
         .Where(e => e.Deleted) 
         .Cast<T>(); 
+0

Questo probabilmente funzionerà, ma prima andrà nel database e prenderà tutti gli oggetti, li getterà in una lista e poi li filtrerà in memoria. La soluzione che ho trovato invia la condizione al database, cioè ottieni solo gli elementi con il valore di flag corretto. – Vladimir

+0

Anche in questo caso, come ho detto a Vladimir, come può funzionare, a meno che un IDeletable non sia applicabile a un T – War

10

Questa è una soluzione di lavoro:

public TEntity GetByUserID(Guid userID, params Include<TEntity>[] includes) 
{ 
    var query = this.DbSet; 
    query = Where<IDeletableEntity>(query, x => !x.IsDeleted); 
    return query 
     .FirstOrDefault(x => x.UserID == userID); 
} 

public static IQueryable<TEntity> Where<TPredicateWellKnownType>(IQueryable<TEntity> query, Expression<Func<TPredicateWellKnownType, bool>> predicate) 
{ 
    if (typeof(TEntity).IsImplementationOf<TPredicateWellKnownType>()) 
    { 
     query = ((IQueryable<TPredicateWellKnownType>)query) 
      .Where(predicate) 
      .Cast<TEntity>(); 
    } 
    return query; 
} 
+0

Questo non può funzionare se TPredicateWellKnownType non è compatibile con TEntity ... ecco perché ottengo un'eccezione in tal senso – War

+0

Absolute genio! Il problema che stavo avendo era trovare la sintassi per permettere al compositore di gestire la fusione senza cercare di passare quella conoscenza al motore SQL e senza modificare il tipo di query. Soluzione molto elegante per qualcosa che sembra ovvio quando lo vediamo fatto per noi, ma sembra troppo brutto per tentare noi stessi. –

-1

Se tutti DbSets ha proprietà 'UserID' quindi creare un'altra interfaccia denominata 'IUserID' e quindi provare questo codice:

protected TEntity GetByUserID<TEntity>(Guid userID) where TEntity : class 
    { 
     var user = this.Set<TEntity>() 
      .ToList() 
      .Cast<IDeletableEntity>() 
      .Where(u => (!u.IsDeleted)) 
      .Cast<IUserID>() 
      .Where(u => (u.UserID == userID)) 
      .FirstOrDefault(); 
     return user; 
    } 
+1

Ora si sta eseguendo l'intera operazione in memoria, piuttosto che nel database. – Servy

+0

@Servy Questo può essere risolto rimuovendo metodo .ToList(), giusto? – Junior

+0

È poi anche necessario assicurarsi che il resto del codice funziona correttamente quando gestito da un provider di EF, ma sì, il 'ToList' costringerà il codice per tutti corrono in memoria. – Servy

Problemi correlati