【发布时间】:2018-02-13 21:24:41
【问题描述】:
由于 EFCore 要求您显式创建连接实体,因此我正在努力寻找使此代码更易于管理的方法。我正在关注本教程(4部分系列,这部分和下一部分是相关的):https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-2-hiding-as-ienumerable/
它将连接类隐藏为私有 ICollection,然后使用连接的实体填充另一个 ICollection,因此它的工作方式与 EF6 类似。这是我跟踪人们拥有的汽车的实现:
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; }
}
我的映射:
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");
});
还有一些种子数据:
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();
}
如果我拉回包括如下汽车在内的人员列表,它会起作用并输出数据:
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}");
}
}
但是,如果我尝试在调试器中查看 Cars 集合结果,则 VS2017 崩溃,这不是很好,但在代码级别它似乎可以工作。
但是,假设我想将列表过滤为仅是奥迪司机,以下结果为 0:
var audiDrivers = db.Persons.Include("PersonCars.Car").Where(x => x.Cars.Any(c => c.Manufacturer == "Audi"));
该系列文章主要侧重于改进添加/删除,并没有提及过滤。我想要这个功能,但我希望能够通过他们的Cars 过滤Persons。
如果我公开PersonCars 收藏,那么我可以这样做:
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();
总结一下:
如果导航属性像文章建议的那样是私有的,是否可以过滤查找?
查看Car集合时为什么VS2017会崩溃?
有没有更好的方法来做到这一点,就像在 EF6 中一样简单?
【问题讨论】:
-
嗯,有趣的想法,但它似乎只是让你的生活变得更加困难并且很可能无法预测的糖。我刚刚开始学习使用映射表。总之祝你好运
-
我喜欢embrace the join table :)
-
不要在部分有效的 hack 上浪费时间。等待 EF Core 支持。
-
@Ivan Stoev,你能在这里看看我的问题吗:stackoverflow.com/questions/54025974/…。和这个类似。
-
@w0051977 我看过了。但反应是一样的——远离这种黑客行为。 EF Core 还不支持它,所以你会得到一个问题 - 在这里工作,而不是在那里工作。主要问题是,当您隐藏实际导航属性时,您将失去 L2E 查询中的“导航”支持。未映射的集合看起来类似于导航属性,但实际上并非如此,因此 L2E 查询中的任何使用都会产生异常。
标签: c# entity-framework entity-framework-core