【问题标题】:Hiding join entity on many to many relationships在多对多关系上隐藏连接实体
【发布时间】: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 中一样简单?

【问题讨论】:

  • 嗯,有趣的想法,但它似乎只是让你的生活变得更加困难并且很可能无法预测的糖。我刚刚开始学习使用映射表。总之祝你好运
  • 不要在部分有效的 hack 上浪费时间。等待 EF Core 支持。
  • @Ivan Stoev,你能在这里看看我的问题吗:stackoverflow.com/questions/54025974/…。和这个类似。
  • @w0051977 我看过了。但反应是一样的——远离这种黑客行为。 EF Core 还不支持它,所以你会得到一个问题 - 在这里工作,而不是在那里工作。主要问题是,当您隐藏实际导航属性时,您将失去 L2E 查询中的“导航”支持。未映射的集合看起来类似于导航属性,但实际上并非如此,因此 L2E 查询中的任何使用都会产生异常。

标签: c# entity-framework entity-framework-core


【解决方案1】:

我个人会直接与连接实体合作。它可能不是“漂亮”,但它是最直接的方法。最终,开发和维护代码将变得更加容易和快捷。我认为,您在开发的早期就使用替代方法遇到了这些基本问题,这一事实证明了这一点。

我的建议是:使用 EF Core,而不是反对它。它不像 EF6 那样成熟,但它最终会到达那里。

【讨论】:

  • 与 ef6 相比,手动添加/删除连接实体条目可能会变得非常冗长,您可以只分配一个集合。
猜你喜欢
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 2013-12-28
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
相关资源
最近更新 更多