2016-09-22 77 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需要一個具有與類型參數兼容的多態結果類型的表達式參數。」

現在,如果你會注意到,我以兩種不同的方式實現它,首先是顯式加載相關實體,其次是有兩個不同的查詢,我的問題是,有沒有一種方法可以加載我的對象圖表和篩選實體我「包括」只有一個單一的旅程從數據庫?

謝謝。

+0

在這兩個例子中,只有2個查詢到數據庫。第一個爲酒店,然後爲客房和預訂。你還想要什麼? – sachin

+0

爲什麼不在'Include()'中包含所有內容?類似於:'context.Hotels。包括( 「Rooms.Reservations」)'? – haim770

+0

@sachin包含其他相關實體,並在可能的情況下使用單次訪問數據庫對這些相關實體進行過濾/排序。 –

回答

3

有過濾包括實體雙向的。

  • 利用投影(見@Eldho答案)
  • 使用第三方庫

免責聲明:我是這個項目的所有者Entity Framework Plus

的EF +查詢IncludeFilter允許輕鬆過濾包含的實體。

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

在引擎蓋下,庫完成了一個投影。

百科:EF+ Query Include Filter

編輯:答案subquestion

你幾乎做到了。房間已包含並過濾,但您未包含預訂。

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是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

嘗試使用強類型映射到dto或viewmodel,像http://stackoverflow.com/a/12410349/ 1876572 – Eldho

+0

看看這也是 – Eldho