Come filtro “Includere” entità in entity framework?

Entità:

    public class Room
    {
        public Room()
        {
            this.Reservations = new HashSet<Reservation>();
        }

        public int Id { get; set; }

        public decimal Rate { get; set; }

        public int HotelId { get; set; }

        public virtual Hotel Hotel { get; set; }

        public virtual ICollection<Reservation> Reservations { get; set; }
    }

    public class Hotel
    {
        public Hotel()
        {
            this.Rooms = new HashSet<Room>();
        }

        public int Id { get; set; }

        public string Name { get; set; }

        public virtual ICollection<Room> Rooms { get; set; }
    }

    public class Reservation
    {
        public int Id { get; set; }

        public DateTime StartDate { get; set; }

        public DateTime EndDate { get; set; }

        public string ContactName { get; set; }

        public int RoomId { get; set; }

        public virtual Room Room { get; set; }
    }

  public class ExecutiveSuite : Room
  {
  }

  public class DataContext : DbContext
    {
        public DbSet<Hotel> Hotels { get; set; }

        public DbSet<Reservation> Reservations { get; set; }

        public DbSet<Room> Rooms { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Room>()
                .HasKey(r => r.Id)
                .HasRequired(r => r.Hotel)
                .WithMany(r => r.Rooms)
                .HasForeignKey(r => r.HotelId);

            modelBuilder.Entity<Hotel>()
                .HasKey(h => h.Id);

            modelBuilder.Entity<Room>()
                .HasMany(r => r.Reservations)
                .WithRequired(r => r.Room)
                .HasForeignKey(r => r.RoomId);

        }
    }

Il codice del client(applicazione console):

static void Main(string[] args)
        {
            //initialize and seed the database
            using (var context = new DataContext())
            {
                var hotel = new Hotel { Name = "Grand Seasons Hotel" };
                var r101 = new Room { Rate = 79.95M, Hotel = hotel };
                var es201 = new ExecutiveSuite { Rate = 179.95M, Hotel = hotel };
                var es301 = new ExecutiveSuite { Rate = 299.95M, Hotel = hotel };

                var res1 = new Reservation
                {
                    StartDate = DateTime.Parse("3/12/2010"),
                    EndDate = DateTime.Parse("3/14/2010"),
                    ContactName = "Roberta Jones",
                    Room = es301
                };
                var res2 = new Reservation
                {
                    StartDate = DateTime.Parse("1/18/2010"),
                    EndDate = DateTime.Parse("1/28/2010"),
                    ContactName = "Bill Meyers",
                    Room = es301
                };
                var res3 = new Reservation
                {
                    StartDate = DateTime.Parse("2/5/2010"),
                    EndDate = DateTime.Parse("2/6/2010"),
                    ContactName = "Robin Rosen",
                    Room = r101
                };

                es301.Reservations.Add(res1);
                es301.Reservations.Add(res2);
                r101.Reservations.Add(res3);

                hotel.Rooms.Add(r101);
                hotel.Rooms.Add(es201);
                hotel.Rooms.Add(es301);

                context.Hotels.Add(hotel);
                context.SaveChanges();
            }

            using (var context = new DataContext())
            {
                context.Configuration.LazyLoadingEnabled = false;
                //Assume we have an instance of hotel
                var hotel = context.Hotels.First();

                //Explicit loading with Load() provides opportunity to filter related data 
                //obtained from the Include() method 
                context.Entry(hotel)
                       .Collection(x => x.Rooms)
                       .Query()
                       .Include(y => y.Reservations)
                       .Where(y => y is ExecutiveSuite && y.Reservations.Any())
                       .Load();

                Console.WriteLine("Executive Suites for {0} with reservations", hotel.Name);

                foreach (var room in hotel.Rooms)
                {
                    Console.WriteLine("\nExecutive Suite {0} is {1} per night", room.Id,
                                      room.Rate.ToString("C"));
                    Console.WriteLine("Current reservations are:");
                    foreach (var res in room.Reservations.OrderBy(r => r.StartDate))
                    {
                        Console.WriteLine("\t{0} thru {1} ({2})", res.StartDate.ToShortDateString(),
                                          res.EndDate.ToShortDateString(), res.ContactName);
                    }
                }
            }

            Console.WriteLine("Press <enter> to continue...");
            Console.ReadLine();
        }



using ( var context = new DataContext() )
{

        //context.Configuration.LazyLoadingEnabled = false;

        //Assume we have an instance of hotel
        var hotel = context.Hotels.First();
        var rooms = context.Rooms.Include( r => r.Reservations ).Where( r => r is ExecutiveSuite && r.Reservations.Any() ).Where( r => r.Hotel.Id == hotel.Id );
        Console.WriteLine( "Executive Suites for {0} with reservations", hotel.Name );

        foreach ( var room in hotel.Rooms )
        {
           Console.WriteLine( "\nExecutive Suite {0} is {1} per night", room.Id,
                             room.Rate.ToString( "C" ) );
           Console.WriteLine( "Current reservations are:" );
           foreach ( var res in room.Reservations.OrderBy( r => r.StartDate ) )
           {
              Console.WriteLine( "\t{0} thru {1} ({2})", res.StartDate.ToShortDateString(),
                                res.EndDate.ToShortDateString(), res.ContactName );
           }
        }
     }

Ho cercato di progettare e mettere in un anonimo oggetto:

       var hotel = context.Hotels.Select(h =>
        new 
        {   
            Id = h.Id,
            Name = h.Name,
            Rooms = h.Rooms.Where(r => r.Reservations is ExecutiveSuite && r.Reservations.Any())
        }).First();

ma mi da un’eccezione: “DbIsOfExpression richiede un argomento di espressione con un polimorfici tipo di risultato che è compatibile con il tipo di argomento.”

Ora, se si nota, l’ho implementato in due modi diversi, il primo è stato esplicitamente carico le relative entità, la seconda è avere due query diverse, la mia domanda sarebbe, c’è un modo posso caricare il mio oggetto grafico e filtrare i soggetti “Includere”, con un solo viaggio dal database?

  • In entrambi esempio, ci sono solo 2 query al database. La prima per Hotel e poi per Camere e Prenotazioni. Cosa vuoi di più?
  • Perché non include tutto il Include()? Qualcosa come: context.Hotels.Include("Rooms.Reservations")?
  • Includere altre entità correlate e quindi ordinamento/filtro quelle relative entità utilizzando un unico viaggio al database, se possibile.
  • Includere altre entità correlate e quindi ordinamento/filtro quelle relative entità utilizzando un unico viaggio al database, se possibile.
  • Provare e vedere
  • che si farebbe carico di tutti i relativi entitties che contrariamente al vero obiettivo. Ho fatto una modifica btw. grazie per la risposta però. 🙂



3 Replies
  1. 11

    Ci sono due modi per filtrare includere Entità.

    • Utilizzando una proiezione (Vedi @Eldho risposta)
    • Utilizzando una libreria di terze parti

    Disclaimer: io sono il proprietario del progetto Entity Framework Plus

    EF+ Query IncludeFilter permette di filtrare facilmente incluso entità.

    context.Entry(hotel)
           .Collection(x => x.Rooms)
           .Query()
           .IncludeFilter(y => y.Reservations
                                .Where(z => z is ExecutiveSuite && z.Reservations.Any())
           .Load();

    Sotto il cofano, la biblioteca fa esattamente una proiezione.

    Wiki: EF+ Query Includono il Filtro

    MODIFICA: Risposta subquestion

    È quasi fatto. Le camere erano inclusi e filtrato, ma non includono le riserve.

    var hotel = context.Hotels
        //Include only executive suite with a reservation
        .IncludeFilter(x => x.Rooms.Where(y => y is ExecutiveSuite && y.Reservations.Any()))
        //Include only reservation from executive suite
        .IncludeFilter(x => x.Rooms.Where(y => y is ExecutiveSuite).Select(z => z.Reservations))
        .First();

    EDIT: Risposta Commento

    Come possiamo includere multilivello proprietà includono il filtro

    È possibile includere multilivello specificando ogni percorso (uno per IncludeFilter)

    Così qry.Include("Rooms.Hotel") diventare:

    qry.IncludeFilter(x => x.Rooms)
       .IncludeFilter(x => x.Rooms.Select(y => y.Hotel))
    • Ho provato ad usarlo senza esplicitamente carico e che non sembra funzionare. => contesto.hotel.IncludeFilter(h => h.camere.Dove(x è ExecutiveSuite && x.Le prenotazioni.Qualsiasi())) Perché è così? Sto ottenendo un risultato diverso. Sto usando correttamente
    • Ho appena fatto una prova e tutto sembra funzionare. Che risultato hai ottenuto? Una limitazione di questa funzionalità è stata precedentemente caricata entità correlate sarà sempre inclusa (anche se esso non soddisfa IncludeFilter predicato). Se si desidera, è anche possibile segnalare il problema sul nostro GitHub forum per rendere più facile per tenere traccia di utilizzo di Overflow dello Stack: github.com/zzzprojects/EntityFramework-Plus/issues
    • Che cosa si intende per “senza l’esplicito di carico”? È necessario utilizzare .Load() o .ToList() (o qualsiasi altro LINQ immediata metodo)
    • Ho qui un repo, se avete tempo si poteva guardare e vedere il risultato che sto cercando di ottenere. Perché potrei essere utilizzando le api, il modo sbagliato e non ci sarebbe alcun bisogno di file per un bug. Grazie! 🙂 github.com/randelramirez/EF6_LoadingEntitiesAndNavigation nome del Progetto, è FilteringAndOrderingRelatedEntities
    • Fammi sapere se la nuova risposta sta lavorando. Posso ottenere lo stesso risultato da parte mia.
    • sì, funziona e vorrei anche ricordare che ho bisogno di disattivare il caricamento pigro per ottenere il risultato corretto. Grazie! 🙂
    • Come possiamo includere multilivello proprietà includono il filtro. Ex di cui sopra di risposta, si desidera caricare l’elenco di Sale e ogni lista deve avere Reservarion proprietà dell’oggetto associato. Come nelle normali ef come – qry.Include(“Le Camere.Hotel”);

  2. 8

    Di notare che non è attualmente possibile filtrare le entità correlate sono caricati. Includere sempre la volontà di portare in tutte le entità correlate Riferimento Msdn

    Richiesta questa caratteristica qui

    Per filtrare bambino collezione si può provare a select che per il modello in forma anonima o di proiezione.

    var anonymousProjection = dbContext.CustomerEntity
                                     .Where(c => ! c.IsDeleted)
                                     .Select(x=> new 
                                      {
                                           customers = x,
                                           orders = x.Orders.Where(h=>h.IsDeleted)
                                      }).ToList();

    Simili risposte

    • Ho fatto una modifica, sto diventando un’eccezione quando cerco di progetto in un oggetto anonimo
    • Provare una forte digitato , come mappa per dto o viewmodel come questo stackoverflow.com/a/12410349/1876572
    • date un’occhiata anche questo
  3. 0

    Stavo pensando di portare un nuovo punto di vista a questo.
    Anche se questo non risolve il problema, potrebbe aiutare.
    Utilizzando AutoMapper, è possibile filtrare le collezioni prima di metterli in oggetto di destinazione. Ho impostato la mia soluzione in modo che tutto ciò che viene mappato in DTOs prima di qualsiasi azione, quindi sto usando AutoMapper come un filtro per chi Comprende.
    Funziona come un fascino…

Lascia un commento