it-swarm.it

Entity-Framework C #: come posso combinare un .Find e .Include su un oggetto modello?

Sto facendo il tutorial di pratica mvcmusicstore. Ho notato qualcosa durante la creazione dello scaffold per il gestore album (aggiungi elimina modifica).

Voglio scrivere codice elegantemente, quindi sto cercando il modo pulito per scrivere questo.

Cordiali saluti, sto rendendo il negozio più generico:

Album = Articoli

Generi = Categorie

Artista = Marchio

Ecco come viene recuperato l'indice (generato da MVC):

var items = db.Items.Include(i => i.Category).Include(i => i.Brand);

Ecco come viene recuperato l'elemento per l'eliminazione:

Item item = db.Items.Find(id);

Il primo riporta tutti gli articoli e popola la categoria e i modelli di marca all'interno del modello di articolo. Il secondo, non popola la categoria e il marchio.

Come posso scrivere il secondo per fare la ricerca E popolare ciò che è dentro (preferibilmente in 1 riga) ... teoricamente - qualcosa del tipo:

Item item = db.Items.Find(id).Include(i => i.Category).Include(i => i.Brand);
130
Ralph N

Devi prima utilizzare Include(), quindi recuperare un singolo oggetto dalla query risultante:

Item item = db.Items
              .Include(i => i.Category)
              .Include(i => i.Brand)
              .SingleOrDefault(x => x.ItemId == id);
142
Dennis Traub

La risposta di Dennis sta usando Include e SingleOrDefault. Quest'ultimo fa un giro circolare nel database.

Un'alternativa è utilizzare Find, in combinazione con Load, per il caricamento esplicito di entità correlate ...

Di seguito n esempio MSDN :

using (var context = new BloggingContext()) 
{ 
  var post = context.Posts.Find(2); 

  // Load the blog related to a given post 
  context.Entry(post).Reference(p => p.Blog).Load(); 

  // Load the blog related to a given post using a string  
  context.Entry(post).Reference("Blog").Load(); 

  var blog = context.Blogs.Find(1); 

  // Load the posts related to a given blog 
  context.Entry(blog).Collection(p => p.Posts).Load(); 

  // Load the posts related to a given blog  
  // using a string to specify the relationship 
  context.Entry(blog).Collection("Posts").Load(); 
}

Naturalmente, Find ritorna immediatamente senza effettuare una richiesta al negozio, se tale entità è già caricata dal contesto.

61
Learner

Non ha funzionato per me. Ma l'ho risolto facendo così.

var item = db.Items
             .Include(i => i.Category)
             .Include(i => i.Brand)
             .Where(x => x.ItemId == id)
             .First();

Non so se questa è una soluzione ok. Ma l'altro che Dennis mi ha dato ha dato un errore bool in .SingleOrDefault(x => x.ItemId = id);

0
Johan

Devi lanciare IQueryable su DbSet

var dbSet = (DbSet<Item>) db.Set<Item>().Include("");

return dbSet.Find(id);

0
Rafael R. Souza

Non esiste un modo semplice per filtrare con una ricerca. Ma ho trovato un modo vicino per replicare la funzionalità, ma per favore prendi nota di alcune cose per la mia soluzione.

Questa soluzione consente di filtrare genericamente senza conoscere la chiave primaria in .net-core

  1. Trova è fondamentalmente diverso perché ottiene l'entità se è presente nel tracciamento prima di eseguire una query sul database.

  2. Inoltre, può filtrare per oggetto in modo che l'utente non debba conoscere la chiave primaria.

  3. Questa soluzione è per EntityFramework Core.

  4. Ciò richiede l'accesso al contesto

Ecco alcuni metodi di estensione da aggiungere che ti aiuteranno a filtrare in base alla chiave primaria

    public static IReadOnlyList<IProperty> GetPrimaryKeyProperties<T>(this DbContext dbContext)
    {
        return dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
    }

    //TODO Precompile expression so this doesn't happen everytime
    public static Expression<Func<T, bool>> FilterByPrimaryKeyPredicate<T>(this DbContext dbContext, object[] id)
    {
        var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var body = keyProperties
            // e => e.PK[i] == id[i]
            .Select((p, i) => Expression.Equal(
                Expression.Property(parameter, p.Name),
                Expression.Convert(
                    Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"),
                    p.ClrType)))
            .Aggregate(Expression.AndAlso);
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }

    public static Expression<Func<T, object[]>> GetPrimaryKeyExpression<T>(this DbContext context)
    {
        var keyProperties = context.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var keyPropertyAccessExpression = keyProperties.Select((p, i) => Expression.Convert(Expression.Property(parameter, p.Name), typeof(object))).ToArray();
        var selectPrimaryKeyExpressionBody = Expression.NewArrayInit(typeof(object), keyPropertyAccessExpression);

        return Expression.Lambda<Func<T, object[]>>(selectPrimaryKeyExpressionBody, parameter);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this DbSet<TEntity> dbSet, DbContext context, object[] id)
        where TEntity : class
    {
        return FilterByPrimaryKey(dbSet.AsQueryable(), context, id);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this IQueryable<TEntity> queryable, DbContext context, object[] id)
        where TEntity : class
    {
        return queryable.Where(context.FilterByPrimaryKeyPredicate<TEntity>(id));
    }

Una volta che hai questi metodi di estensione puoi filtrare in questo modo:

query.FilterByPrimaryKey(this._context, id);
0
johnny 5