LINQ to Entities:对多对多关系执行联接(代码优先)

时间:2018-11-12 17:03:50

标签: c# linq ef-code-first linq-to-entities code-first

我有以下模型:

[Table("Experiments")]
public class Experiment
{
    ...

    public virtual ICollection<ExperimentType> ExperimentTypes { get; set; }

    public Experiment()
    {
        ExperimentTypes = new List<ExperimentType>();
    }
}

[Table("ExperimentTypes")]
public class ExperimentType
{
    ...

    public virtual ICollection<Experiment> Experiments { get; set; }

    public ExperimentType()
    {
        Experiments = new List<Experiments>();
    }
}

DbSet包含:

    public DbSet<Experiment> Experiments { get; set; }
    public DbSet<ExperimentType> ExperimentTypes{ get; set; }

这将在SQL上创建一个名为ExperimentExperimentTypes的表。

现在,我想执行LINQ连接,例如:

var query =
   from e in database.Experiments
   join eet in database.ExperimentExperimentTypes on eet.Experiment_Id equals eet.ExperimentType_Id ...

但是很明显,数据库.ExperimentExperimentTypes在代码中无法识别。

为了告诉代码有此表,我做了很多事情,我也尝试创建相应的c#类,但没有得到任何结果。

如何实现?

1 个答案:

答案 0 :(得分:0)

因此,您有两个表:ExperimentExperimentType。这两者之间存在多对多的关系:每个Experiment都是零个或多个ExperimentTypes的实验;每个ExperimentType都是零个或多个Experiments的类型。

这可以在您的类定义中看到。两侧的virtual ICollection<...>表示多对多关系。

在关系数据库中,此多对多关系是使用联结表实现的。但是,在实体框架中,您很少看到联结表。它在您的数据库中,但是,您无法使用DbContext访问它

  

但是我要如何在“实验”和“   如果我无法访问联结表,则为ExperimentTypes?

好吧,维尼熊应该回到他的思考地点。他不想联接表,他希望所有(或一些)Experiments,每个都具有其ExperimentTypes的所有(或部分)。

那么为什么不只使用ICollection<...>查询呢?

var experiments = myDbContext.Experiments
    .Where(experiment => ...)              // only if you don't want all experiments
    .Select(experiment => new
    {   // Select only the properties you actually plan to use
        Id = experiment.Id,
        Name = experiment.Name,
        ...

        // get all or some of its ExperimentTypes
        ExperimentTypes = experiment.ExperimentTypes
             .Where(experimentType => ...)  // only if you don't want all experiment types
             .Select(experimentType => new
             {
                  // again: select only the properties you plan to use
                  Id = experimentType.Id,
                  ...
             })
             .ToList(),
    });

实体框架非常了解,它知道需要使用联结表进行三次联接,并将执行这种三次联接。

在内部,这将是一个 GroupJoin ,您将获得Experiments,每个都有其ExperimentTypes。您甚至可以获得还没有任何实验类型的实验。

如果您确实要使用inner join,则必须使用它们的ExperimentTypes来简化实验。这是通过overload of SelectMany that has a resultSelector as parameter

完成的
// flatten the Experiments with their experimentTypes
var flatInnerJoin = myDbContext.Experiments.SelectMany(experiment => experiment.ExperimentTypes,

    // from each experiment and one of its experimentTypes make one new object
    (experiment, experimentType) => new
    {
        ExperimentId = experiment.Id,
        ExperimentTypeId = experimentType.Id,
        ...
     });
 })

Nota bene!这样,您就不会像标准内部联接中那样获得没有ExperimentTypes的实验。