2016-09-22 4 views
2

Сущность:Как фильтровать объекты «Включить» в структуру сущностей?

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); 

     } 
    } 

Код клиента (консольное приложение):

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); 
      } 
     } 
    } 

Я попытался проектированием и ввод его в анонимном объекте:

 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(); 

, но я получаю исключение: «DbIsOfExpression требует аргумент выражения с полиморфным типом результата, который совместим с аргументом типа».

Теперь, если бы вы заметили, я реализовал его двумя разными способами: сначала явным образом загружал связанные объекты, во-вторых, с двумя разными запросами, мой вопрос был бы, есть способ, которым я могу загрузить свой объект графа и фильтровать объекты I «Включить» только с одной поездкой из базы данных?

Спасибо.

+0

В обоих примерах всего две запросы к базе данных. Первый для отеля, а затем для номеров и бронирования. Что еще вы хотите? – sachin

+0

Почему бы не включить 'include' в' Include() '? Что-то вроде: 'context.Hotels.Включить ("Rooms.Reservations") '? – haim770

+0

@sachin Включить другие связанные объекты, а затем фильтровать/сортировать связанные объекты, используя, по возможности, одну поездку в базу данных. –

ответ

3

Существует два способа фильтрации включают Entity.

  • Использование проекции (См @Eldho ответ)
  • Использование библиотеки третьей стороны

Отказ от ответственности: Я владелец проекта Entity Framework Plus

ЭФ + Запрос IncludeFilter позволяют легко фильтровать включенные объекты.

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

Под капотом библиотека выполняет ровно проекцию.

Wiki: EF+ Query Include Filter

EDIT: Ответ подвопрос

Вы почти сделали это. Номера были включены и отфильтрованы, но вы не указали оговорки.

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(); 
+0

Я пробовал использовать его без явно загружаемой информации и, похоже, не работает. => context.hotels.IncludeFilter (h => h.rooms.Where (x is ExecutiveSuite && x.Reservations.Any())) Почему так? Я получаю другой результат. Правильно ли я использую его –

+1

@RandelRamirez, я просто сделал тест, и все работает. Какой результат вы получаете? Одно ограничение этой функции - это ранее загруженные связанные объекты всегда будут включены (даже если они не удовлетворяют предикату IncludeFilter). Если вы хотите, вы также можете сообщить об этой проблеме на нашем форуме GitHub, чтобы упростить отслеживание, чем использование переполнения стека: https://github.com/zzzprojects/EntityFramework-Plus/issues –

+0

Что вы подразумеваете под «без явной загрузки» «? Вам нужно либо использовать .Load(), либо .ToList() (или любой другой метод LINQ) –

1

Обратите внимание, что в настоящее время невозможно фильтровать, какие связанные объекты загружаются. Включать всегда будет приносить все связанные с ним объекты Msdn reference

Запрос эту функцию here

Для того чтобы отфильтровать коллекцию ребенка вы можете попытаться select, что модель или анонимным проекции.

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

Similar answers

+0

Я сделал редактирование, я получаю исключение, когда пытаюсь проецировать его в анонимный объект. –

+0

Попробуйте сильную типизированную карту, например map to dto или viewmodel, такую ​​как http://stackoverflow.com/a/12410349/ 1876572 – Eldho

+0

Посмотрите это также – Eldho

Смежные вопросы