Entités:
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);
}
}
Le code client (application 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 );
}
}
}
J'ai essayé de le projeter et de le mettre dans un objet anonyme:
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();
mais j'obtiens une exception: "DbIsOfExpression nécessite un argument d'expression avec un type de résultat polymorphe compatible avec l'argument de type."
Maintenant, si vous le remarquez, je l'ai implémenté de deux manières différentes, d'abord en chargeant explicitement les entités liées, en second lieu en ayant deux requêtes différentes, ma question serait, est-il possible de charger mon graphique d'objet et de filtrer le entités que j'inclus avec un seul trajet depuis la base de données?
Il existe deux façons de filtrer l'entité.
Avertissement : Je suis le propriétaire du projet Entity Framework Plus
EF + Query IncludeFilter permet de filtrer facilement les entités incluses.
context.Entry(hotel)
.Collection(x => x.Rooms)
.Query()
.IncludeFilter(y => y.Reservations
.Where(z => z is ExecutiveSuite && z.Reservations.Any())
.Load();
Sous le capot, la bibliothèque fait exactement une projection.
Wiki: EF + Query Inclure le filtre
[~ # ~] modifier [~ # ~] : Répondre à la sous-question
Tu l'as presque fait. Les chambres étaient incluses et filtrées, mais vous n'avez pas inclus les réservations.
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: Répondre au commentaire
Comment pouvons-nous inclure des propriétés à plusieurs niveaux avec un filtre d'inclusion
Vous pouvez inclure plusieurs niveaux en spécifiant chaque chemin (un par IncludeFilter)
Donc qry.Include("Rooms.Hotel")
devient:
qry.IncludeFilter(x => x.Rooms)
.IncludeFilter(x => x.Rooms.Select(y => y.Hotel))
Notez qu'il n'est actuellement pas possible de filtrer les entités liées qui sont chargées. Inclure apportera toujours toutes les entités liées référence Msdn
Demander cette fonctionnalité ici
Afin de filtrer la collection enfant, vous pouvez essayer de select
cela pour modéliser ou projection anonyme.
var anonymousProjection = dbContext.CustomerEntity
.Where(c => ! c.IsDeleted)
.Select(x=> new
{
customers = x,
orders = x.Orders.Where(h=>h.IsDeleted)
}).ToList();
Je pensais apporter une nouvelle perspective à cela. Même si cela ne résoudra pas le problème, cela pourrait vous aider. À l'aide d'AutoMapper, vous pouvez filtrer les collections avant de les placer dans l'objet de destination. J'ai configuré ma solution de manière à ce que tout soit mappé dans les DTO avant toute action, donc j'utilise AutoMapper comme filtre pour ces inclus. Fonctionne comme un charme ...