Poiché EFCore richiede di creare in modo esplicito le entità di join, sto cercando di trovare modi per rendere questo codice più gestibile. Sto seguendo questo tutorial (serie in 4 parti, questa parte e la parte successiva sono quelle rilevanti): https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core -2-0-parte-2-nascosto-as-IEnumerable /
Nasconde la classe join come privato ICollection
e quindi popola un'altra ICollection con l'entite unita in modo che funzioni in modo simile a EF6. Ecco la mia implementazione per tenere traccia di ciò che le auto possiedono:
public class Person
{
public Person() =>
Cars = new JoinCollectionFacade<Car, PersonCar>
(PersonCars, pc => pc.Car, c => new PersonCar { Person = this, Car = c });
public int Id { get; set; }
public string Name { get; set; }
private ICollection<PersonCar> PersonCars { get; } = new List<PersonCar>();
[NotMapped]
public ICollection<Car> Cars { get; }
}
public class Car
{
public Car() => Persons = new JoinCollectionFacade<Person, PersonCar>
(PersonCars, pc => pc.Person, p => new PersonCar { Person = p, Car = this });
public int Id { get; set; }
public string Manufacturer { get; set; }
private ICollection<PersonCar> PersonCars { get; } = new List<PersonCar>();
[NotMapped]
public ICollection<Person> Persons { get; }
}
public class PersonCar
{
public Person Person { get; set; }
public int PersonId { get; set; }
public Car Car { get; set; }
public int CarId { get; set; }
}
Le mie mappature:
modelBuilder.Entity<PersonCar>(e =>
{
e.HasKey(t => new { t.PersonId, t.CarId });
e.HasOne(pc => pc.Person).WithMany("PersonCars");
e.HasOne(pc => pc.Car).WithMany("PersonCars");
});
E alcuni dati seme:
using (var db = new ManyDbContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Persons.AddRange(
new Person() { Name = "John" },
new Person() { Name = "Peter" },
new Person() { Name = "Paul" }
);
db.Cars.AddRange(
new Car() { Manufacturer = "Audi" },
new Car() { Manufacturer = "Honda" },
new Car() { Manufacturer = "Mercedes" },
new Car() { Manufacturer = "Ferrai" },
new Car() { Manufacturer = "Porche" }
);
db.SaveChanges();
db.PersonCars.AddRange(
new PersonCar() { PersonId = 1, CarId = 2},
new PersonCar() { PersonId = 1, CarId = 3 },
new PersonCar() { PersonId = 2, CarId = 2 },
new PersonCar() { PersonId = 3, CarId = 1 }
);
db.SaveChanges();
}
Se torno indietro un elenco di persone tra cui macchine come sotto funziona e emette i dati:
var drivers = db.Persons.Include("PersonCars.Car").ToList();
foreach(var person in drivers)
{
foreach(var car in person.Cars)
{
Console.WriteLine($"{person.Name} has a {car.Manufacturer}");
}
}
Tuttavia, se provo a visualizzare i risultati della raccolta di Cars
all'interno del debugger, VS2017 si arresta in modo anomalo, il che non è eccezionale ma a livello di codice sembra funzionare.
Ma, diciamo che voglio filtrare l'elenco per essere solo driver Audi, i risultati di seguito in 0 risultati:
var audiDrivers = db.Persons.Include("PersonCars.Car").Where(x => x.Cars.Any(c => c.Manufacturer == "Audi"));
La serie di articoli si concentra principalmente sul miglioramento dell'aggiunta / rimozione e non menziona il filtraggio. Mi piacerebbe questa funzionalità, ma voglio essere in grado di filtrare le Persons
con le loro Cars
.
Se pubblico la raccolta di PersonCars
allora posso fare:
var audiDrivers = db.Persons
.Include(i => i.PersonCars).ThenInclude(i => i.Car)
.Where(x => x.PersonCars.Select(pc => pc.Car).Any(c => c.Manufacturer == "Audi"))
.ToList();
Quindi per riassumere:
Se la proprietà di navigazione è privata come suggerisce l'articolo, la ricerca può essere filtrata?
Perché VS2017 si blocca quando si guarda alla collezione di Car
?
C'è un modo migliore per fare ciò che è semplice come in EF6?
Personalmente lavorerei direttamente con le entità di join. Potrebbe non essere "carina" ma è l'approccio più diretto. In definitiva sarà più facile e veloce sviluppare e mantenere il tuo codice. Il fatto che tu abbia riscontrato problemi di base con l'approccio alternativo così presto nel tuo sviluppo, penso che lo provi.
Il mio consiglio è: lavorare con EF Core, non contro di esso. Non è maturo come EF6 ma ci arriverà alla fine.