隐藏多对多关系中的连接实体

时间:2018-02-13 12:37:11

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

由于EFCore要求您显式创建连接实体,我试图找到使这些代码更易于管理的方法。我正在学习本教程(4部分系列,本部分和下一部分是相关部分):https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-2-hiding-as-ienumerable/

它将联接类隐藏为私有ICollection,然后使用已连接的entitis填充另一个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"));

该系列文章主要关注改进添加/删除,而不提及过滤。我希望使用该功能,但我希望能够按Persons过滤Cars

如果我将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();

总结一下:

如果导航属性像文章建议的那样是私有的,可以过滤查找吗?

为什么VS2017在查看Car集合时会崩溃?

有没有比EF6更简单的方法呢?

1 个答案:

答案 0 :(得分:1)

我个人会直接与联接实体合作。它可能不是“漂亮”,但它是最直接的方法。最终,开发和维护代码将更容易,更快捷。事实上,你在开发过程中尽早使用替代方法解决了这些基本问题,我认为可以证明这一点。

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